Skip to main content

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 is 10.100.0.2.
  • Docker Compose runs the pinned image with MATILDA_NETWORK_MODE=host so 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 to ssm:SendCommand on that one box.

One-time bootstrap​

Standing up a fresh box:

  1. Provision. In infra/, copy terraform.tfvars.example to terraform.tfvars, then run terraform init and terraform apply. The user-data script installs Docker, Caddy, and WireGuard on first boot. Add the two A records from the dns_records output at your DNS host.

  2. TLS. Fill the REPLACE_* placeholders in infra/Caddyfile, install it at /etc/caddy/Caddyfile, and reload Caddy.

  3. App. As the ubuntu user, run docker login ghcr.io with a personal access token, since the package is private. Copy infra/docker-compose.staging.yml to ~/matilda/docker-compose.yml with a .env based on infra/.env.staging.example. Then:

    docker compose pull && docker compose up -d
  4. iMessage. Bring up WireGuard on both ends using the configs in infra/wireguard/, set BLUEBUBBLES_URL=http://10.100.0.2:1234 and the password, and register the webhook. See Connect iMessage.

  5. Enable CD. In the GitHub repository, create a staging environment with the secret AWS_DEPLOY_ROLE_ARN and the variables AWS_REGION and STAGING_INSTANCE_ID, all output by Terraform. Merges to main now deploy automatically.

Production checklist​

  • MATILDA_DASHBOARD_SECRET and MATILDA_CUSTOMER_SECRET set to long random strings. Defaults are refused on https.
  • MATILDA_PUBLIC_URL set 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_CIDRS for defense in depth. See Security.