Deployment
Everyday deploy: merge to main
Work on the dev branch, open a pull request, merge. The GitHub Actions release workflow builds an immutable image tagged sha-<short>, pushes it to GitHub Container Registry, and deploys it to the staging box over AWS Systems Manager using a short-lived OIDC role. No SSH, no stored AWS keys, no security group changes.
Cutting a pinned release is the same flow triggered by a vX.Y.Z tag, which produces semver image tags.
Rollback
Re-pin an older image in the box's .env and restart:
MATILDA_VERSION=1.5.0
docker compose up -d
Reference topology
Matilda is one SQLite process, so the reference deployment is deliberately a single stateful EC2 box, not a container orchestrator with a managed database.
- Caddy terminates TLS and reverse-proxies to Matilda on loopback. Port 8000 is never public.
- WireGuard links the box to the Mac running BlueBubbles. The box is
10.100.0.1, the Mac is10.100.0.2. - Docker Compose runs the pinned image with
MATILDA_NETWORK_MODE=hostso the container uses the host's WireGuard route and sees real client IPs. - Terraform in the webapp's
infra/folder provisions the instance, the SSM instance profile, and the GitHub OIDC deploy role scoped tossm:SendCommandon that one box.
One-time bootstrap
Standing up a fresh box:
-
Provision. In
infra/, copyterraform.tfvars.exampletoterraform.tfvars, then runterraform initandterraform apply. The user-data script installs Docker, Caddy, and WireGuard on first boot. Add the two A records from thedns_recordsoutput at your DNS host. -
TLS. Fill the
REPLACE_*placeholders ininfra/Caddyfile, install it at/etc/caddy/Caddyfile, and reload Caddy. -
App. As the
ubuntuuser, rundocker login ghcr.iowith a personal access token, since the package is private. Copyinfra/docker-compose.staging.ymlto~/matilda/docker-compose.ymlwith a.envbased oninfra/.env.staging.example. Then:docker compose pull && docker compose up -d -
iMessage. Bring up WireGuard on both ends using the configs in
infra/wireguard/, setBLUEBUBBLES_URL=http://10.100.0.2:1234and the password, and register the webhook. See Connect iMessage. -
Enable CD. In the GitHub repository, create a
stagingenvironment with the secretAWS_DEPLOY_ROLE_ARNand the variablesAWS_REGIONandSTAGING_INSTANCE_ID, all output by Terraform. Merges tomainnow deploy automatically.
Production checklist
MATILDA_DASHBOARD_SECRETandMATILDA_CUSTOMER_SECRETset to long random strings. Defaults are refused on https.MATILDA_PUBLIC_URLset to the https origin so cookies are marked Secure and handoff links are correct.- A webhook token generated on Setup before the box is reachable from the internet.
- Owner emails set so someone can sign in.
- Consider
MATILDA_DASHBOARD_ALLOW_CIDRSfor defense in depth. See Security.