Runbook: Provisioning a device¶
One-command path (recommended) and the manual step breakdown for when something in the middle fails and needs a retry.
Prerequisites¶
- Terraform installed,
jqinstalled .envat the repo root withSSH_USER/SSH_PASSWORDfor the device's default user (see.env.example)CLOUDFLARE_API_TOKENandCLOUDFLARE_ACCOUNT_IDexported for Terraform and fortask credentials:export- The device is booted, on the network, with SSH enabled
- Ansible Vault password at
ansible/.vault-password
0. Register the device¶
Add it to config/devices.yml under the right environment (production,
staging, development, or the lite_environments equivalent):
production:
devices:
<new-12-hex-serial>:
ip_address: "192.168.1.106"
location: "Lab"
This is the only place the device needs to be declared for Terraform.
1. One-command provisioning¶
task setup-device DEVICE=rpi-prod-<serial> IP=192.168.1.106
This is task workflows:setup-device — see
architecture.md for what it chains together. Expect
~10-15 minutes.
2. Verify¶
curl -I https://rpi-prod-<serial>.tagai.uk
# expect a redirect toward Cloudflare Access
task ansible:health-prod # or the matching -dev / -staging / -lite variant
Open https://rpi-prod-<serial>-nodered.tagai.uk in a browser — you should
hit Cloudflare Access first, then the Node-RED editor after authenticating.
Manual step-by-step (if the one-command flow fails partway)¶
Each of these is safe to re-run on its own.
1. Terraform (tunnel + DNS)
cd terraform && terraform plan && terraform apply
2. Export tunnel credentials
task credentials:export DEVICE=rpi-prod-<serial>
3. Provision the device (creates tagai user, installs cloudflared + Node-RED)
task provision:single DEVICE=rpi-prod-<serial> IP=192.168.1.106
4. Generate the MQTT client certificate (one-time: task mqtt:init first,
if the CA doesn't exist yet)
task mqtt:cert DEVICE=rpi-prod-<serial>
5. Sync configuration to the device
task sync:single DEVICE=rpi-prod-<serial> IP=192.168.1.106
6. Deploy via Ansible
task ansible:deploy DEVICE=rpi-prod-<serial>
# add TUNNEL=true to route over the Cloudflare Tunnel instead of the LAN IP
Troubleshooting¶
Device not reachable on LAN
ping 192.168.1.106
sudo nmap -sn 192.168.1.0/24 # find it if the IP changed
SSH connection refused — connect a monitor/keyboard directly and check:
sudo systemctl status ssh && sudo systemctl enable --now ssh
Provisioning failed mid-way — SSH in and check both services:
ssh tagai@rpi-prod-<serial>-ssh.tagai.uk # or the LAN IP directly
sudo systemctl status cloudflared node-red
sudo journalctl -u cloudflared -n 50
sudo journalctl -u node-red -n 50
Domain doesn't resolve / tunnel shows no connections
nslookup rpi-prod-<serial>.tagai.uk
# on the device:
sudo cloudflared tunnel info
sudo systemctl status cloudflared
Fleet-wide health check
task ansible:health # all environments
ansible-playbook -i ansible/inventory/production/hosts.yml ansible/playbooks/healthcheck.yml