Skip to content

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, jq installed
  • .env at the repo root with SSH_USER / SSH_PASSWORD for the device's default user (see .env.example)
  • CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID exported for Terraform and for just 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

just setup-device rpi-prod-<serial> 192.168.1.106

This is just 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

just 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.

3. Print and stick the label

just docs label-print rpi-prod-<serial>

Prints a 40x20 mm label carrying a QR code of https://<device-id>.tagai.uk plus the serial, environment prefix and location, on the Phomemo M110 over USB. Stick it on the enclosure before the device leaves the bench — the serial is otherwise only readable by opening the case or querying the fleet.

Scan the QR with a phone camera before fitting it; it should open the device's Cloudflare Access page. First time on a given Mac, see label-printing-provisioning.md for the one-time driver and CUPS queue setup.

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

just credentials export rpi-prod-<serial>

3. Provision the device (creates tagai user, installs cloudflared + Node-RED)

just provision rpi-prod-<serial> 192.168.1.106

4. Generate the MQTT client certificate (one-time: just mqtt init first, if the CA doesn't exist yet)

just mqtt cert rpi-prod-<serial>

5. Sync configuration to the device

just sync single rpi-prod-<serial> 192.168.1.106

6. Deploy via Ansible

just ansible deploy rpi-prod-<serial>
# add --tunnel 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
Then re-run the specific manual step above that failed.

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

just ansible health           # all environments
ansible-playbook -i ansible/inventory/production/hosts.yml ansible/playbooks/healthcheck.yml