Skip to content

Deploy

There are two deploy targets with very different cadences: the cloud (the VPS) and the edge (the fleet of Pis). This page covers both.

Cloud: push to main, it ships

Cloud deploy — push to main, it ships

  • Deploys are automatic. Pushing to main triggers .github/workflows/deploy-cloud.yml on a self-hosted runner that lives on the VPS.
  • The workflow only fires when relevant paths change: cloud/**, compose.prod.yaml, caddy/**, deploy/**, or the workflow file itself.
  • Watch a run with gh run watch <run-id> or gh run list --workflow=deploy-cloud.yml.

This wiki lives under cloud/ for a reason

The wiki source is at cloud/wiki/ precisely so that (a) it's bundled into the sudo-api image and (b) editing it triggers the deploy. Docs outside cloud/ (like repo-root docs/) do not auto-deploy and are not in the image.

Caddy deploys too — carefully

Caddy runs natively on the host (not in compose). The workflow runs a root-owned wrapper, sudo /usr/local/bin/sudo-deploy-caddy.sh, granted a tightly-scoped NOPASSWD rule. It caddy validates the checked-out caddy/Caddyfile, then graceful-reloads. The bootstrap-vps.yml workflow is the from-scratch installer (SSH/rsync).

Manual escape hatch

make prod-up        # compose.prod.yaml up -d --build
make prod-status    # ps
make prod-logs      # logs -f
make prod-rebuild   # force-recreate

These need deploy/.env. There is no test suite — verify by reading code, then deploy + smoke-test (/chat, /integrations, /admin/settings, and now /docs).

Git workflow: main + rebase only

For this repo: commit straight to main, rebase rather than merge, no long-lived feature branches. Auto-deploy from main is the intended loop.

Edge: tagged releases, self-updating Pis

The Pi fleet updates itself from a published bundle.

Edge release — tagged builds, self-updating Pis

  • The release workflow fires on a v* tag push, not on plain pushes to main.
  • Check the live version with curl sudohomes.com/manifest.json.
  • Each Pi checks roughly hourly (with jitter), updates, runs a 180s health gate, and auto-rolls-back if unhealthy. Operators throttle via manifest.json (paused / rollout_pct / min_check_sec).

Don't force-update a live device

After a tag, let devices pick up the update on their own cadence. Don't force an update on a live device unless explicitly asked.

Test edge changes on real hardware before tagging

Don't commit/tag/push sudoedge changes until they've been tested on a physical device — Pi audio/hardware bugs don't show up any other way.

See RELEASE.md for the full release playbook and the auto-update internals.