Provisioning Devices
Provisioning adds a device to your Watchgrid fleet. The device gets a WireGuard VPN connection, the Watchgrid agent, Magic DNS resolution, and SSH CA trust — all in one step.
Supported Platforms
- Raspberry Pi (all models, including Pi 5)
- Ubuntu Server 20.04+
- Debian 11+
- Any Linux system with
systemdandbash
Supported architectures: amd64, arm64, arm (armhf/armv7).
One-Line Provisioning
Run this command on the device you want to add:
Replace YOUR_SERVER with your Watchgrid server's domain name or IP. HTTPS is required — the provisioning script refuses http:// URLs to prevent binary tampering during download. For local-only development use WATCHGRID_ALLOW_HTTP=1 as an explicit override.
Options
| Flag | Description |
|---|---|
--k3s |
Also install K3s (lightweight Kubernetes) |
--update |
Only update the agent binary (skip VPN setup) |
--force |
Force reprovisioning (clear existing WireGuard config and keys) |
--token TOKEN |
Onboarding token for tenant assignment |
--siteid ID |
Assign the device directly to a site during first onboarding |
Examples:
# Basic provisioning
curl -s https://watchgrid.example.com/provision.sh | sudo bash -s https://watchgrid.example.com
# With K3s installation
curl -s https://watchgrid.example.com/provision.sh | sudo bash -s https://watchgrid.example.com --k3s
# Update agent only
curl -s https://watchgrid.example.com/provision.sh | sudo bash -s https://watchgrid.example.com --update
# With onboarding token (assigns to specific tenant)
curl -s https://watchgrid.example.com/provision.sh | sudo bash -s https://watchgrid.example.com --token abc123
# Assign to site 5 during onboarding
curl -s https://watchgrid.example.com/provision.sh | sudo bash -s https://watchgrid.example.com --token abc123 --siteid 5
# Force a clean reprovision (wipes WireGuard config)
curl -s https://watchgrid.example.com/provision.sh | sudo bash -s https://watchgrid.example.com --force
# Local dev only — bypass HTTPS check
WATCHGRID_ALLOW_HTTP=1 bash provision.sh http://localhost:8080
What Provisioning Does
The provisioning script performs these steps automatically:
- Detects architecture — amd64, arm64, or arm
- Downloads the agent binary from your server (correct architecture)
- Generates WireGuard keys (or reuses existing ones if reprovisioning)
- Registers with the server — sends device info and public key
- Configures WireGuard — sets up the VPN tunnel to the server
- Installs the agent as a systemd service (
watchgrid-agent) - Configures SSH CA trust — downloads the CA public key for certificate-based SSH
- Optionally installs K3s with the
--k3sflag - Runs provisioning profiles — executes any tag-matched scripts (see Provisioning Profiles)
Approving Devices
After a device is provisioned, it appears in the Dashboard as a pending authorization:
- Go to the Dashboard
- Look for the Pending Agent Authorizations section at the top
- You'll see the device ID and its WireGuard public key
- Click Approve & Assign IP to admit the device to the VPN
- The device gets a VPN IP (e.g.,
100.64.1.x) and becomes reachable
Denied devices are rejected and cannot connect to the VPN.
Onboarding via the Web UI
You can also onboard devices through the web interface:
- Go to Devices → click Onboard Device
- The modal shows a provisioning command with your server URL
- Copy the command and run it on the target device
- Onboarding tokens can lock a device to a specific tenant
Reprovisioning
If your server is reinitialized or you need to refresh a device's connection:
Running the same provisioning command again:
- Reuses existing WireGuard keys (does not generate new ones)
- Updates the agent binary to the latest version
- Re-registers with the server
- Preserves the device's identity
Raspberry Pi 5 Note
On a Raspberry Pi 5 with K3s (--k3s flag), the provisioning script detects if cgroups need to be enabled. If so, it updates /boot/firmware/cmdline.txt and reboots the device automatically. After reboot, run the provisioning command again to complete the K3s installation.
Verifying a Provisioned Device
On the device, check the agent service:
Verify VPN connectivity:
Verify Magic DNS:
Troubleshooting
| Problem | Solution |
|---|---|
| Device stuck on "pending" | Check that WG_SERVER_ENDPOINT is reachable from the device |
| WireGuard handshake fails | Verify port 51820/udp is open on the server |
| Agent won't start | Check logs: journalctl -u watchgrid-agent -f |
DNS not resolving .wg |
Ensure DNS = 100.64.1.254 is in the device's WireGuard config |
| Architecture mismatch | The script auto-detects arch; verify with uname -m |
| Update fails with "checksum mismatch" | Rebuild agent binaries on the server — checksums are generated by scripts/build-agent.sh and must match the served binary |
Security
HTTPS requirement
The provisioning script refuses http:// server URLs to prevent man-in-the-middle attacks during binary download. Always run your Watchgrid server behind a TLS-terminating reverse proxy (nginx, Caddy, etc.) in production. The script also runs automatically after a K3s reboot — the stored server URL must be HTTPS for this continuation step to succeed.
Agent self-update integrity
When the server triggers a remote agent update:
- SHA-256 checksum — the agent downloads
watchgrid-agent-{arch}.sha256and compares it against the binary hashed in-flight; mismatches abort the update. - Ed25519 signature — if a public key is configured (via
WATCHGRID_UPDATE_PUBKEYenv var or the build-time-ldflags "-X main.agentUpdatePublicKeyHex=..."flag), the agent also downloadswatchgrid-agent-{arch}.sigand verifies an Ed25519 signature over the binary. If verification fails, the binary is deleted and the update is aborted.
To enable signature verification in production:
# Generate a signing key pair (run once, store private key securely)
openssl genpkey -algorithm ed25519 -out agent-signing.pem
openssl pkey -in agent-signing.pem -pubout -outform DER | tail -c 32 | xxd -p -c 32 > agent-signing.pub.hex
# Sign each binary in the build pipeline
openssl pkeyutl -sign -inkey agent-signing.pem -rawin -in watchgrid-agent-amd64 \
-out watchgrid-agent-amd64.sig
# Embed the public key at build time
go build -ldflags="-X main.agentUpdatePublicKeyHex=$(cat agent-signing.pub.hex)" ./...
# Or set at runtime on the device
export WATCHGRID_UPDATE_PUBKEY=$(cat agent-signing.pub.hex)
If no public key is configured, the agent logs a warning and falls back to checksum-only verification.
TLS certificate pinning (SPKI)
The agent supports pinning the server's TLS certificate by its SPKI (Subject Public Key Info) SHA-256 hash. Set WATCHGRID_SERVER_SPKI to a comma-separated list of hex-encoded SHA-256 SPKI hashes. The agent rejects any server whose certificate does not match.
# Get the SPKI hash of your server's certificate
openssl s_client -connect watchgrid.example.com:443 </dev/null 2>/dev/null \
| openssl x509 -noout -pubkey \
| openssl pkey -pubin -outform DER \
| openssl dgst -sha256 -hex | awk '{print $2}'
export WATCHGRID_SERVER_SPKI=<hash-from-above>
K3s container registry
Devices running K3s pull container images from registry.wg:5000 via the WireGuard-encrypted tunnel. The containerd registry configuration uses plain http:// endpoints; WireGuard provides transport-layer encryption. No TLS certificate bypass flags are set in the registry config.