https://github.com/kpeacocke/piclaw
Ansible runbook for Raspberry Pi 5 + AI HAT+ 2 (Hailo-10H) — enforces a known-good state with Hailo runtime, local LLM via Hailo Ollama, and OpenClaw on every run.
https://github.com/kpeacocke/piclaw
ai-hat ansible hailo hailo10h homelab local-llm ollama openclaw raspberry-pi raspberry-pi-5 raspberry-pi-5-ai
Last synced: 22 days ago
JSON representation
Ansible runbook for Raspberry Pi 5 + AI HAT+ 2 (Hailo-10H) — enforces a known-good state with Hailo runtime, local LLM via Hailo Ollama, and OpenClaw on every run.
- Host: GitHub
- URL: https://github.com/kpeacocke/piclaw
- Owner: kpeacocke
- License: apache-2.0
- Created: 2026-04-05T01:21:48.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-06-02T00:21:08.000Z (about 1 month ago)
- Last Synced: 2026-06-14T07:34:50.627Z (22 days ago)
- Topics: ai-hat, ansible, hailo, hailo10h, homelab, local-llm, ollama, openclaw, raspberry-pi, raspberry-pi-5, raspberry-pi-5-ai
- Language: Python
- Homepage: https://github.com/kpeacocke/piclaw
- Size: 344 KB
- Stars: 0
- Watchers: 0
- Forks: 1
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Codeowners: .github/CODEOWNERS
- Security: .github/SECURITY.md
- Support: .github/SUPPORT.md
- Notice: NOTICE
Awesome Lists containing this project
README
# Pi 5 + AI HAT+ 2 OpenClaw Stack
[](https://github.com/kpeacocke/piclaw/actions/workflows/ansible-ci.yml)
[](LICENSE)
[](https://www.python.org/)
[](https://ansible.com/)
Deploy a **privacy-preserving AI inference gateway** on Raspberry Pi 5 + AI HAT+ 2 using Ansible. Run large language models locally on edge hardware with hardware acceleration (Hailo NPU) and expose them via an **OpenAI-compatible API**.
## What You Get
- **OpenClaw Gateway** — OpenAI-compatible REST API for local LLM inference
- Web UI (Canvas) for interactive chat
- Streaming completions support
- No cloud dependency, no API keys sent externally
- **Hailo NPU Acceleration** — Hardware-accelerated inference on AI HAT+ 2 (Hailo-10H)
- **Ollama Integration** — Local model management (pull, load, cache)
- **Flexible Profiles**
- `local-safe`: Fully local inference (privacy-first)
- `external-power`: Fallback to cloud providers (OpenAI, etc.)
- **Idempotent Ansible Playbooks** — Repeatable, validated deployments
- **CI/CD Ready** — Pre-commit hooks, linting, molecule testing, GitHub Actions
## Quick Start
### Prerequisites
- **Hardware**: Raspberry Pi 5 + AI HAT+ 2 (Hailo-10H)
- **OS**: Raspberry Pi OS Trixie 64-bit (fresh install recommended)
- **Control Host**: Ansible 2.20+ with Python 3.14+
### 1. Configure Target Host & Tailscale Auth Key
**Edit `inventories/prod/hosts.yml`** to point to your Pi:
```yaml
pi5-node:
ansible_host: 192.168.1.50 # ← Update to your Pi's IP (or Tailscale IP after first deploy)
ansible_user: pi
```
**Obtain a Tailscale auth key** (required for bootstrap):
1. Go to https://login.tailscale.com/admin/settings/keys
2. Click **Generate auth key**
3. ✅ Check "Reusable" (allows multiple devices)
4. Copy the key: `tskey-auth-`
5. Store it in AWX as a secret credential or encrypted inventory variable
### 2. Run Playbooks (in order)
```bash
# 1. Bootstrap: Tailscale, OS packages, firmware, Hailo runtime
ansible-playbook playbooks/bootstrap.yml -l pi5
# 2. Deploy: Ollama + OpenClaw gateway
ansible-playbook playbooks/openclaw.yml -l pi5
# 3. Verify: Functional tests (chat roundtrip, health checks)
ansible-playbook playbooks/verify.yml -l pi5
```
### 3. Use the Gateway
OpenClaw listens on `127.0.0.1:18789` and is accessible via **Tailscale mesh VPN** for secure, encrypted device-to-device access.
#### Access via Tailscale (Recommended)
1. **Join the Tailscale network**:
- Install [Tailscale](https://tailscale.com/download) on your control machine
- Sign in with your Tailscale account
- Your Pi will auto-join during bootstrap with the auth key you provided
2. **Find the Pi's Tailscale IP**:
```bash
tailscale list # Shows all devices; look for your pi5 hostname
# Example: pi5 (100.100.100.50) to authenticate; created Apr 20, 2026
```
3. **Access OpenClaw via Tailscale**:
```bash
# Web UI (Canvas)
open http://100.100.100.50:18789/__openclaw__/canvas/
# CLI
ssh pi@100.100.100.50 openclaw chat
# OpenAI-compatible API
curl http://100.100.100.50:18789/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "ollama/llama3.2:3b",
"messages": [{"role": "user", "content": "Hello from Tailscale!"}]
}'
```
#### Legacy SSH Port-Forward (if Tailscale unavailable)
```bash
ssh -L 18789:127.0.0.1:18789 pi@
# Then access at http://localhost:18789/...
```
## Deployment Modes
### Standalone (CLI)
```bash
# Copy repo to your control machine
git clone https://github.com/kpeacocke/piclaw.git
cd piclaw
# Set up local dev environment
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt
ansible-galaxy collection install -r collections/requirements.yml
# Run playbooks
ansible-playbook playbooks/bootstrap.yml -l pi5
ansible-playbook playbooks/openclaw.yml -l pi5
ansible-playbook playbooks/verify.yml -l pi5
```
### AWX (Orchestration)
Import job templates from `awx/job_templates.yml`. AWX manages:
- Inventory + host vars
- Survey-driven variable overrides
- Secrets injection
- Job history and audit logs
- Execution on execution nodes
See [AWX Setup](#awx-setup) for details.
## Configuration
### Core Variables
Repository defaults live in each role's `defaults/main.yml`. Override them in one of:
1. `group_vars/pi5.yml` — For all Pi 5s in the inventory
2. AWX inventory/group/host vars — Per-host overrides
3. Playbook `extra_vars` — Runtime via CLI or survey
### Key Options
**OpenClaw Profile** (`openclaw_profile`)
- `local-safe` (default) — All inference runs on Hailo NPU via Ollama
- `external-power` — Fallback to cloud API (e.g., OpenAI)
**Model Selection** (`hailo_model`)
- Default: `llama3.2:3b` (3B parameter, strongest installed Hailo-backed local model)
- Options: Any Ollama model compatible with Hailo's quantization constraints
**Sanitizer Proxy** (`use_sanitizer_proxy`)
- Enabled by default — Adds safety layer to Ollama responses
- Set to `false` to skip
**External Provider** (for `external-power`)
```yaml
openclaw_external_provider_name: custom
openclaw_external_provider_base_url: https://api.openai.com/v1
openclaw_external_provider_api: openai-completions
openclaw_external_provider_model: gpt-4-mini
openclaw_external_provider_api_key_env: OPENAI_API_KEY
```
**Tailscale Mesh VPN** (`tailscale_auth_key`)
- **Required** — Obtain a reusable auth key from https://login.tailscale.com/admin/settings/keys
1. Go to **Settings > Keys**
2. Click **Generate auth key**
3. ✅ Check "Reusable" (allows multiple devices)
4. Copy the key: `tskey-auth-`
- Provide it via an AWX custom credential, an encrypted AWX inventory/group variable, or an Ansible vault file
- Enables secure, encrypted mesh VPN access to all services
- Replaces LAN allowlists with Tailscale network boundary
**Additional Tailscale Options**
```yaml
tailscale_device_name: "pi5" # Custom hostname on Tailscale network
tailscale_accept_dns: true # Let Tailscale manage system DNS
tailscale_advertise_routes: false # Advertise routes to other devices
tailscale_advertise_exit_node: false # Don't route all traffic through Pi
```
## What Gets Installed & Validated
Each playbook enforces a known-good state:
### `bootstrap.yml`
- **Tailscale** — Mesh VPN agent (joins your Tailscale network via auth key)
- OS packages and security updates
- Firmware updates + reboot handling
- PCIe Gen 3.0 optimization
- Hailo runtime and driver installation
- Hardware probe (PCIe enumeration check)
### `openclaw.yml`
- Hailo Ollama integration (model pull and cache)
- OpenClaw installer and systemd service
- Provider configuration (local or external)
- Optional sanitizer proxy
- Port 18789 is available and responding
### `verify.yml`
- Smoke test: Chat roundtrip (request → response)
- Health checks: Gateway and Ollama health endpoints
- Doctor verification: System readiness report
## Repository Structure
```
.
├── playbooks/
│ ├── bootstrap.yml # OS setup, firmware, Hailo runtime
│ ├── openclaw.yml # Ollama, gateway, provider config
│ └── verify.yml # Smoke tests and health checks
├── roles/
│ ├── base/ # OS packages, EEPROM, PCIe config
│ ├── hailo/ # Hailo runtime and driver
│ ├── hailo_ollama/ # Ollama + model integration
│ ├── openclaw/ # OpenClaw gateway + systemd
│ └── validate/ # Verification and smoke tests
├── inventories/
│ └── prod/hosts.yml # Target Pi host and group vars
├── group_vars/
│ ├── pi5.yml # Local overrides
│ └── pi5.vault.yml.example
├── awx/ # Job template and survey specs
├── collections/
│ └── requirements.yml # Ansible collection dependencies
├── molecule/
│ └── default/ # Role testing (syntax/lint focused)
├── tests/
│ └── test_molecule_default.py
└── .github/
├── workflows/ # CI/CD automation (lint, test, release)
└── ISSUE_TEMPLATE/ # Bug and feature templates
```
## Development
### Local Setup
```bash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
pip install -r requirements-dev.txt
ansible-galaxy collection install -r collections/requirements.yml
pre-commit install
```
### Quality Checks
```bash
# All checks in one go
pre-commit run --all-files
# Individual checks
ansible-lint # Playbook lint
yamllint . # YAML syntax
ansible-playbook --syntax-check playbooks/*.yml # Ansible syntax
molecule test # Role testing
pytest # Python unit tests
```
All checks pass by default before commit (pre-commit hooks).
### Running on Hardware
Test code changes on a real Pi before submitting a PR:
```bash
# Get Pi's Tailscale IP: tailscale list | grep pi5
# Assuming Pi's Tailscale IP is 100.100.100.50
# SSH to the Pi via Tailscale and check status
ssh pi@100.100.100.50 openclaw status
# Re-run a single playbook from your control machine
ansible-playbook playbooks/openclaw.yml -l pi5 -v
# Check logs on the Pi
ssh pi@100.100.100.50 journalctl -u openclaw -n 50 -f
```
See [CONTRIBUTING.md](CONTRIBUTING.md) for branching, PRs, and release workflow.
## AWX Setup
### Quick Import
Job templates, surveys, and credential examples are defined in `awx/job_templates.yml`, `awx/surveys.yml`, and `awx/credentials.yml`. Create them in AWX via:
1. **API**: `awx-cli` or `curl` with the YAML spec
2. **UI**: Create manually, use the specs as reference
3. **Automation**: Third-party AWX provisioning tools
### Key Configuration
**Tailscale Secret Storage**
Do not keep the Tailscale auth key in a survey. Store it once in AWX and let the bootstrap job consume it automatically.
Recommended options:
1. **Custom AWX credential**
- Create a credential type with one secret field: `tailscale_auth_key`
- Paste the field schema into `Input configuration`
- Paste the variable injection into `Injector configuration` or `Output configuration`, depending on your AWX version
- Attach that credential to `piclaw-bootstrap`
- Use `awx/credentials.yml` as the source-controlled reference
2. **Encrypted inventory/group variable**
- Put `tailscale_auth_key` in the AWX inventory or group vars UI
- Mark it as a secret if your AWX version supports secret inputs for that path
The bootstrap template no longer prompts for this value by default.
**Galaxy Credential Requirement**
Attach an Ansible Galaxy or Automation Hub credential to your AWX organization. Without it, collection sync skips and jobs fail with `couldn't resolve module/action 'community.docker...'`.
**Receptor Topology**
Ensure direct connection from AWX Receptor to execution nodes:
```
awx-task ↔ awx-receptor ↔ execution-node
```
Do **not** relay through a hop with a different Receptor version.
**Runtime Path**
AWX's `purge_old_stdout_files` task requires `/var/lib/awx/job_status/` in `awx-task`. Add to the entrypoint:
```bash
mkdir -p /var/lib/awx/job_status
```
### Execution Order
Run templates in this order:
1. `piclaw-bootstrap` — OS and runtime setup
2. `piclaw-openclaw` — Ollama + gateway (surveys: profile, model, provider)
3. `piclaw-verify` — Validation and smoke tests
See `awx/surveys.yml` for survey question specs and defaults.
## Security & Secrets
- **Never commit secrets** to git (passwords, API keys, hostnames)
- **Vault encryption** available: `ansible-vault encrypt group_vars/pi5.vault.yml`
- **AWX secrets**: Prefer credential plugins or encrypted inventory vars; avoid surveys for persistent secrets
- **API keys** (for external providers): Injected at runtime via environment variables, not in playbooks
For sensitive deployments:
- Manage inventory in a private repo
- Use AWX's credential store for API keys
- Rotate secrets regularly
## GitHub Automation
| Workflow | Trigger | Purpose |
|----------|---------|---------|
| `ansible-ci` | PR, push | Lint, syntax check, secret scan |
| `scheduled-verify` | Weekly | Catch regressions early |
| `pr-labeler` | PR | Auto-label by changed paths |
| `label-sync` | Push to main | Sync issue labels from `.github/labels.yml` |
| `release-drafter` | PR merge | Draft semantic release notes |
| `publish-release` | Manual | Tag release and publish |
| `scorecard` | Weekly | OpenSSF security posture |
## Contributing
1. **Fork** the repository
2. **Create a branch** for your feature/fix
3. **Run quality checks** locally (`pre-commit run --all-files`)
4. **Test on hardware** if changes affect deployment
5. **Submit a PR** with a clear, conventional commit title (`feat:`, `fix:`, `chore:`, etc.)
6. **Request review** from maintainers
See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed workflow and PR expectations.
## Support
- **Discussions**: Ask questions in [GitHub Discussions](https://github.com/kpeacocke/piclaw/discussions)
- **Issues**: Report bugs or request features via [GitHub Issues](https://github.com/kpeacocke/piclaw/issues)
- **Security**: Report vulnerabilities via [GitHub Security Advisories](https://github.com/kpeacocke/piclaw/security/advisories/new) (private)
## License
Licensed under the **Apache License 2.0**. See [LICENSE](LICENSE) for details.
## Acknowledgments
- Raspberry Pi Foundation (hardware platform)
- Hailo (NPU hardware and runtime)
- Ollama (model management)
- OpenClaw (LLM inference gateway)
- Ansible (infrastructure automation)