Skip to content

Docker Compose Deployment

Docker Compose is the primary way to run AnvilBase. The same docker-compose.yml powers local dev and production; a docker-compose.prod.yml overlay applies production hardening.

Production bring-up

Terminal window
git clone https://github.com/paxtone-studio/anvilbase.git
cd anvilbase
cp .env.example .env
  1. Fill secrets — generate unique values for every # Generate: field. Use a secrets manager rather than hand-editing on the box where possible. See Configuration.
  2. Set production knobs in .env:
    Terminal window
    ANVILBASE_MTLS_ENABLED=true # after generating certs
    # relocate/close the Traefik dashboard port
    # configure your domain + ACME for Traefik
    MINIO_KMS_SECRET_KEY=<operator-managed key>
  3. Generate mTLS certs (if enabling):
    Terminal window
    docker/mtls/generate-certs.sh
  4. Start with the production overlay:
    Terminal window
    docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
  5. Verify:
    Terminal window
    curl -s https://<your-host>/health/services | jq .

Set COMPOSE_FILE in .env to make the overlay stick: COMPOSE_FILE=docker-compose.yml:docker-compose.prod.yml.

TLS at the edge

Traefik terminates TLS. Point your domain at the host and configure the ACME (Let’s Encrypt) resolver, or mount your own certificates. Serve everything over HTTPS and redirect HTTP → HTTPS. Disable the dashboard in production. See Network Security.

Storage backend

MinIO by default; switch to RustFS via the overlay in .env:

Terminal window
COMPOSE_FILE=docker-compose.yml:docker-compose.rustfs.yml

See Installation → Storage backend.

Postgres image

AnvilBase ships one Postgres image, built by docker compose up from docker/postgres/Dockerfile (FROM postgres:15-bookworm — vanilla OSS plus PGDG-apt and source-built extensions, no third-party Postgres base).

If you are upgrading from a pre-1.1 deployment whose data directory was created by the older supabase-based image, run the one-time migration before it boots on this image — see Upgrades → Postgres image and scripts/migrate-postgres-to-owned.sh. New deployments need no action.

Day-to-day operations

Terminal window
# Status & health
docker compose ps
curl -s https://<host>/health/services | jq .
# Logs (structured JSON)
docker compose logs -f control-plane
docker compose logs -f --tail 100 control-plane auth webhooks
# Restart a single service (no dependents)
docker compose up -d --no-deps control-plane
# Stop / start the stack
docker compose stop
docker compose up -d

Persisting data

Application state lives in named Docker volumes (Postgres data, MinIO objects).

Terminal window
docker compose down # stop, KEEP volumes (safe)
docker compose down -v # stop and DELETE volumes (destroys all data)

Back up the underlying volumes and/or use logical backups (Backups & Restore). For tight RPO, enable PITR.

Resource sizing

  • Budget RAM for Postgres (shared_buffers, work_mem), the JVM-free Rust services (small), realtime (BEAM), and MinIO. The control plane’s per-project REST pools are ~2–5 MB each.
  • Postgres max_connections is the key ceiling — see Scaling for the replicas × active_projects × pool_max math.

Updating

Terminal window
docker compose pull
docker compose exec control-plane /app/migrate # platform migrations
docker compose up -d --no-deps control-plane auth webhooks console
curl -s https://<host>/health/services | jq .

Full procedure (including major versions and backups): Upgrades.

Single-node limits

Compose on one host is simple and robust for many workloads, but it’s a single point of failure. Scale out while staying on Compose — a warm Postgres standby, then a stateful/stateless split across a few hosts behind a load balancer — as described in Scaling. Running more than one control-plane replica requires a reachable Valkey for shared rate limiting. The Helm chart is experimental (untested) and not a production HA path today.

Next: Configuration.