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
git clone https://github.com/paxtone-studio/anvilbase.gitcd anvilbasecp .env.example .env- Fill secrets — generate unique values for every
# Generate:field. Use a secrets manager rather than hand-editing on the box where possible. See Configuration. - 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 TraefikMINIO_KMS_SECRET_KEY=<operator-managed key> - Generate mTLS certs (if enabling):
Terminal window docker/mtls/generate-certs.sh - Start with the production overlay:
Terminal window docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d - Verify:
Terminal window curl -s https://<your-host>/health/services | jq .
Set
COMPOSE_FILEin.envto 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:
COMPOSE_FILE=docker-compose.yml:docker-compose.rustfs.ymlSee 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
# Status & healthdocker compose pscurl -s https://<host>/health/services | jq .
# Logs (structured JSON)docker compose logs -f control-planedocker 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 stackdocker compose stopdocker compose up -dPersisting data
Application state lives in named Docker volumes (Postgres data, MinIO objects).
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_connectionsis the key ceiling — see Scaling for thereplicas × active_projects × pool_maxmath.
Updating
docker compose pulldocker compose exec control-plane /app/migrate # platform migrationsdocker compose up -d --no-deps control-plane auth webhooks consolecurl -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.