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.
- 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
- 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+
Edit inventories/prod/hosts.yml to point to your Pi:
pi5-node:
ansible_host: 192.168.1.50 # ← Update to your Pi's IP (or Tailscale IP after first deploy)
ansible_user: piObtain a Tailscale auth key (required for bootstrap):
- Go to https://login.tailscale.com/admin/settings/keys
- Click Generate auth key
- ✅ Check "Reusable" (allows multiple devices)
- Copy the key:
tskey-auth-<base64> - Store it in AWX as a secret credential or encrypted inventory variable
# 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 pi5OpenClaw listens on 127.0.0.1:18789 and is accessible via Tailscale mesh VPN for secure, encrypted device-to-device access.
-
Join the Tailscale network:
- Install Tailscale on your control machine
- Sign in with your Tailscale account
- Your Pi will auto-join during bootstrap with the auth key you provided
-
Find the Pi's Tailscale IP:
tailscale list # Shows all devices; look for your pi5 hostname # Example: pi5 (100.100.100.50) to authenticate; created Apr 20, 2026
-
Access OpenClaw via Tailscale:
# 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!"}] }'
ssh -L 18789:127.0.0.1:18789 pi@<pi5-ip>
# Then access at http://localhost:18789/...# 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 pi5Import 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 for details.
Repository defaults live in each role's defaults/main.yml. Override them in one of:
group_vars/pi5.yml— For all Pi 5s in the inventory- AWX inventory/group/host vars — Per-host overrides
- Playbook
extra_vars— Runtime via CLI or survey
OpenClaw Profile (openclaw_profile)
local-safe(default) — All inference runs on Hailo NPU via Ollamaexternal-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
falseto skip
External Provider (for external-power)
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_KEYTailscale Mesh VPN (tailscale_auth_key)
- Required — Obtain a reusable auth key from https://login.tailscale.com/admin/settings/keys
- Go to Settings > Keys
- Click Generate auth key
- ✅ Check "Reusable" (allows multiple devices)
- Copy the key:
tskey-auth-<base64>
- 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
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 PiEach playbook enforces a known-good state:
- 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)
- 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
- Smoke test: Chat roundtrip (request → response)
- Health checks: Gateway and Ollama health endpoints
- Doctor verification: System readiness report
.
├── 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
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# 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 testsAll checks pass by default before commit (pre-commit hooks).
Test code changes on a real Pi before submitting a PR:
# 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 -fSee CONTRIBUTING.md for branching, PRs, and release workflow.
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:
- API:
awx-cliorcurlwith the YAML spec - UI: Create manually, use the specs as reference
- Automation: Third-party AWX provisioning tools
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:
- 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 configurationorOutput configuration, depending on your AWX version - Attach that credential to
piclaw-bootstrap - Use
awx/credentials.ymlas the source-controlled reference
- Encrypted inventory/group variable
- Put
tailscale_auth_keyin 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:
mkdir -p /var/lib/awx/job_statusRun templates in this order:
piclaw-bootstrap— OS and runtime setuppiclaw-openclaw— Ollama + gateway (surveys: profile, model, provider)piclaw-verify— Validation and smoke tests
See awx/surveys.yml for survey question specs and defaults.
- 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
| 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 |
- Fork the repository
- Create a branch for your feature/fix
- Run quality checks locally (
pre-commit run --all-files) - Test on hardware if changes affect deployment
- Submit a PR with a clear, conventional commit title (
feat:,fix:,chore:, etc.) - Request review from maintainers
See CONTRIBUTING.md for detailed workflow and PR expectations.
- Discussions: Ask questions in GitHub Discussions
- Issues: Report bugs or request features via GitHub Issues
- Security: Report vulnerabilities via GitHub Security Advisories (private)
Licensed under the Apache License 2.0. See LICENSE for details.
- Raspberry Pi Foundation (hardware platform)
- Hailo (NPU hardware and runtime)
- Ollama (model management)
- OpenClaw (LLM inference gateway)
- Ansible (infrastructure automation)