Production Checklist
Work through this before exposing an AnvilBase deployment. It consolidates the controls from the Security section into a single pre-flight list.
Secrets
- Every secret is unique and randomly generated (
openssl rand -hex 32+). - Secrets live in a secrets manager (Vault, AWS/GCP Secrets Manager), not a committed file.
-
.envis never in version control. -
CONTROL_PLANE_SECRET≥ 32 chars;SECRET_KEY_BASE≥ 64 chars and not the public dev default (it signs realtime/Phoenix cookies & tokens — a known value forges sessions). Compose requires it (${SECRET_KEY_BASE:?}) and a prod realtime release refuses to boot on the dev literal. -
MINIO_KMS_SECRET_KEYis an operator-managed key (not the dev default). -
INTERNAL_SECRETis set, identical across control-plane/auth/realtime/deno, and not the public dev literal. It gates the control-plane/internal/*routes (incl. the per-project JWT-secret fetch), so a weak/known value is a full auth-bypass. The control-plane and a prod realtime release refuse to boot on the known dev literal; Compose requires it (${INTERNAL_SECRET:?}) and Helmrequireds it — so this is enforced, but verify it’s a real secret. - A secret rotation schedule is defined (rotation).
Network
- Only Traefik (ports 80/443) is exposed publicly; internal service ports are unpublished.
- External TLS configured (Let’s Encrypt or your certs); HTTP → HTTPS.
- Traefik dashboard disabled / not internet-reachable.
- Services share a private network (
anvilbase-net); Postgres/Valkey/MinIO are not directly exposed. - mTLS enabled between services (
ANVILBASE_MTLS_ENABLED=true) once traffic leaves a single host. - Webhook SSRF guard relied upon (not disabled).
Database
- RLS enabled on all user tables — verify:
sql SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname='public'; - Every table has a tested policy (RLS sandbox).
-
statement_timeoutset (e.g. 30s) to bound runaway queries. - Per-project connection cap sized (
ANVILBASE_PROJECT_POOL_MAX) under Postgresmax_connections. - Application uses the
anvilbaserole, never the Postgres superuser. - Data volume encrypted at rest (LUKS / cloud volume encryption).
Postgres pg_hba hardening (existing deployments)
The raw-SQL exec_sql RPC already runs on a least-privileged authenticator
connection that cannot reach another project’s database. As defense-in-depth,
fresh Postgres inits also require scram (not trust) for the privileged
anvilbase owner over loopback, so a hypothetical reached-anvilbase context
cannot open a passwordless dblink hairpin into a sibling platform_<id>
database. This auto-applies only on a fresh data dir. To apply it to an
already-initialized deployment, prepend these two rules above the existing
host all all 127.0.0.1/32 trust line in $PGDATA/pg_hba.conf (first match
wins), then reload:
host all anvilbase 127.0.0.1/32 scram-sha-256host all anvilbase ::1/128 scram-sha-256SELECT pg_reload_conf();API
- Rate limits in effect (Valkey reachable — required for >1 replica).
- Max body size bounded to prevent memory exhaustion.
- API key scopes understood and enforced in client/server split.
- JWT expiry set sensibly (default 3600s).
- CORS restricted to your origins in production.
- Management credentials (admin token / PATs) tightly held; treat each as deployment-admin (RBAC).
Storage
- SSE-S3 encryption active (per-project keys via
MINIO_KMS_SECRET_KEY). - Bucket isolation verified (
bucket-<project_id>per project). - Max file size enforced; dangerous content types handled (content-type safety).
Auth
- Password minimum length ≥ 8 (
min_password_length). - Email verification required where appropriate; SMTP configured + tested.
- OAuth client secrets stored encrypted (they are, by default — verify masking).
- Magic-link / reset link expiry is short.
Backups & recovery
- Logical backups scheduled (Backups & Restore).
- PITR enabled if you need tight RPO (PITR).
- A restore has been tested (not just configured) on a staging copy.
- Backup encryption keys (
WALG_LIBSODIUM_KEY) stored safely and separately.
Observability
- Health checks wired (
/health,/health/services) to your uptime monitor. - Logs aggregated (structured JSON) and searchable.
- Alerts on: 5xx error rate, auth failure spikes, DB connection saturation, storage near quota, cert expiry, and the rate-limiter degraded warning (Monitoring, Scaling).
- Audit log export scheduled to immutable off-box storage if required for compliance (Audit Logs).
Upgrades
- Component versions pinned (Compose tags / Helm
anvilbaseVersion). - Upgrade + rollback procedure rehearsed (Upgrades).
When every box is checked, you’re ready. Keep this list with your runbooks and re-run it after major changes.
Next: Backups & Restore.