Admin Stack
This repository contains the deployment configuration for the Admin interface.
Prerequisites
- Same host as Stoat, rootless user with linger.
- Ansible + podman + WireGuard userspace tools installed.
- GCP credentials for Secret Manager.
- Public DNS record for
admin.${DOMAIN}in Google Cloud DNS pointing to the WG server IP (or no record at all if usingtls internal). - Cloud DNS service account provisioned with
roles/dns.adminand stored in Secret Manager.
First Deploy
Run the bootstrap script:
./scripts/bootstrap.sh
Adding a new WG client
- Edit
wg_clientsinansible/inventory.yml(or your overriding group_vars). - Re-run the wireguard playbook:
ansible-playbook -i ansible/inventory.yml ansible/wireguard.yml - Distribute the new client config from
./generated/clients/<name>.conf.
Removing a WG client
- Remove the client from
wg_clients. - Re-run the playbook.
- Verify in
wg show wg0that the peer is gone.
Rotating the WG server key
Rotating the server key is disruptive — every client config must be regenerated and redistributed.
- Remove the old key from Secret Manager or create a new version.
- Re-run the wireguard playbook.
Rotating the Caddy DNS service account key
- Generate a new key with
gcloud iam service-accounts keys create. - Push to Secret Manager as a new version.
- Re-run bootstrap step 6 to materialize the key.
- Restart Caddy (
podman compose restart caddy). - Disable the old key with
gcloud iam service-accounts keys disableand finally delete after a grace period.
Redeploy Procedure
podman compose pullpodman compose up -d./scripts/verify.sh
Secret Rotation Procedure
- Update the secret in GCP Secret Manager (e.g.
admin-env). - Materialize the
.envfile again. podman compose up -dto recreate containers with the new environment.
SQLite Backup and Recovery Procedure
Backups are handled by scripts/sqlite-backup.sh.
- To restore, stop the
admin-apicontainer. - Replace the live
admin.dbin theadmin-sqlitenamed volume with the snapshot file. - Restart the
admin-apicontainer.