Skip to content

Configuration

AnvilBase is configured entirely through environment variables (.env for Compose, Helm values for Kubernetes). This page explains how configuration works and the security-relevant settings; the complete variable catalog is in Reference → Configuration.

Where configuration lives

  • Docker Compose.env in the repo root (copied from .env.example).
  • Kubernetes — Helm values.yaml / a referenced Secret (Kubernetes).
  • Per-project settings (auth, SMTP, eIDAS, OAuth, quotas) are not env vars — they’re configured at runtime via the console/API and stored per project.

Generating secrets

Terminal window
openssl rand -hex 24 # passwords (48 hex chars)
openssl rand -hex 32 # signing secrets (CONTROL_PLANE_SECRET, AUTH_SECRET)
openssl rand -hex 64 # SECRET_KEY_BASE (Phoenix needs >= 64 chars)
# MinIO SSE-S3 KMS key (key-id:base64):
echo "anvilbase-prod:$(openssl rand -base64 32)"

Required secrets

VariablePurpose
POSTGRES_PASSWORDPostgres superuser
ANVILBASE_DB_PASSWORDapplication DB user (used by all services)
CONTROL_PLANE_SECRETsigns PATs; encrypts the secrets vault (≥ 32 chars)
AUTH_SECRETBetter Auth session signing
SECRET_KEY_BASEPhoenix realtime (≥ 64 chars)
MINIO_ROOT_PASSWORDstorage admin
MINIO_KMS_SECRET_KEYSSE-S3 bucket encryption (required — see below)
VALKEY_PASSWORDcache auth

MINIO_KMS_SECRET_KEY is required. Project provisioning calls put_bucket_encryption on every new bucket; without a KMS key MinIO rejects it and project creation rolls back. Format: <key-id>:<base64(32 bytes)>.

Management authentication

VariableRole
ANVILBASE_ADMIN_TOKENthe bootstrap admin token for /api/v1 (root credential). If unset, that bootstrap path is disabled and you authenticate with PATs only.

CONTROL_PLANE_SECRET also HMAC-verifies Personal Access Tokens and signs platform session JWTs (console login). Mint scoped PATs with anvilbase token mint --name ci --scope admin|deploy|read once the bootstrap token is configured. See Reference → Management API.

Ports

Host ports default to a dedicated 39xxx block to avoid collisions; container- internal ports stay at conventional defaults. Override any with *_PORT. Full table: Reference → Ports.

Security-relevant toggles

VariableDefaultEffect
ANVILBASE_MTLS_ENABLEDfalsemutual TLS between internal services (enable for prod)
ANVILBASE_WALG_ENABLEDtrueWAL archiving for PITR (a no-op until an S3 target is configured)
ALLOW_INTERNAL_WEBHOOK_TARGETSfalsewhen truthy, disables the SSRF guard blocking webhook/function delivery to private/loopback IPs. Leave off in production.
ANVILBASE_CORS_ORIGINScompose: http://localhost:39004; bare release builds: unset = cross-origin disabledexact-origin CORS allow-list for browser apps (the console). Release builds fail closed without it.
ANVILBASE_TRUSTED_PROXIESRFC1918 + loopbackCIDRs whose X-Forwarded-For is trusted for client-IP resolution (credential brute-force guard)
ANVILBASE_METRICS_TOKENunsetoptional Bearer gate on the control-plane / webhooks /metrics endpoints
TRAEFIK_DASHBOARD_PORT39081relocate/close in production
IMGPROXY_KEY / IMGPROXY_SALTunsetenable signed image URLs (Image Transformations)
BACKUP_OFFSITE_ENDPOINT / _BUCKET / _ACCESS_KEY / _SECRET_KEY (+ optional _REGION)unsetasynchronous offsite/DR copy of every successful project backup to a secondary S3 target (Backups & Restore). Set all four or none — partial config fails at boot.

Tuning

VariableDefaultEffect
ANVILBASE_PROJECT_POOL_MAX20max Postgres connections per project pool
ANVILBASE_POOL_IDLE_TIMEOUT_SECS300scale-to-zero idle window (seconds): release an idle project’s pooled DB + Valkey cache resources after this long, restoring them lazily on next use. 0 disables eviction (keep pools warm). Garbage falls back to 300.
ANVILBASE_AUDIT_RETENTION_DAYS90default window (days) for the on-demand audit prune (POST /admin/audit/prune, anvilbase audit prune) over the audit_log table and the auth-event PGMQ archive. The nightly scheduled prune uses its own cron literal (90; change via UPDATE cron.job) — this var does not alter the schedule. Values <1 fall back to 90. See Audit log retention.
ANVILBASE_DLQ_RETENTION_DAYS30retention window (days) for the webhook dead-letter queue nightly purge. Values <1 fall back to 30.
WEBHOOK_MAX_RETRIES5webhook delivery attempts
WEBHOOK_TIMEOUT30per-attempt webhook timeout (seconds)
WEBHOOK_RETRY_BASE_DELAY10base backoff seconds between webhook attempts
WEBHOOK_MAX_CONCURRENT10max concurrent in-flight webhook deliveries
RUST_LOGinfocontrol-plane log level (debug for troubleshooting)

This page covers the security-relevant and commonly-tuned settings. For the complete catalog — every variable for every service (read replicas, logical replication, scheduled base backups, the Deno worker supervisor, Supavisor, observability, MFA, and more) — see Reference → Configuration.

Watch the connection math: replicas × active_projects × ANVILBASE_PROJECT_POOL_MAX must stay under Postgres max_connections (Scaling).

Resource limits (Compose)

Every service in docker-compose.yml declares a deploy.resources.limits block (cpus + memory) that Docker Compose v2 enforces on docker compose up (it maps to --cpus / --memory). These are env-tunable ceilings with generous defaults: each default sits comfortably above realistic usage, so the limits only contain a runaway container — they do not right-size normal operation, and an existing deployment is never throttled or OOM-killed by the defaults.

Override per service in .env with ANVILBASE_<SERVICE>_CPUS / ANVILBASE_<SERVICE>_MEM_LIMIT:

  • Small hosts can lower them — but do so carefully: a *_MEM_LIMIT set below a container’s real memory usage makes Docker OOM-kill it on the next up. Postgres is the most sensitive (it holds every project’s data) — keep its limit well above shared_buffers + work_mem × concurrency.
  • Heavy load can raise them.
ServiceCPUs var (default)Memory var (default)
postgresANVILBASE_POSTGRES_CPUS (4)ANVILBASE_POSTGRES_MEM_LIMIT (8g)
control-planeANVILBASE_CONTROL_PLANE_CPUS (4)ANVILBASE_CONTROL_PLANE_MEM_LIMIT (2g)
realtimeANVILBASE_REALTIME_CPUS (2)ANVILBASE_REALTIME_MEM_LIMIT (2g)
supavisorANVILBASE_SUPAVISOR_CPUS (2)ANVILBASE_SUPAVISOR_MEM_LIMIT (1g)
denoANVILBASE_DENO_CPUS (2)ANVILBASE_DENO_MEM_LIMIT (2g)
webhooksANVILBASE_WEBHOOKS_CPUS (2)ANVILBASE_WEBHOOKS_MEM_LIMIT (1g)
authANVILBASE_AUTH_CPUS (2)ANVILBASE_AUTH_MEM_LIMIT (1g)
minioANVILBASE_MINIO_CPUS (2)ANVILBASE_MINIO_MEM_LIMIT (2g)
valkeyANVILBASE_VALKEY_CPUS (2)ANVILBASE_VALKEY_MEM_LIMIT (2g)
traefikANVILBASE_TRAEFIK_CPUS (2)ANVILBASE_TRAEFIK_MEM_LIMIT (512m)
imgproxyANVILBASE_IMGPROXY_CPUS (2)ANVILBASE_IMGPROXY_MEM_LIMIT (1g)
prometheusANVILBASE_PROMETHEUS_CPUS (2)ANVILBASE_PROMETHEUS_MEM_LIMIT (2g)
grafanaANVILBASE_GRAFANA_CPUS (1)ANVILBASE_GRAFANA_MEM_LIMIT (1g)
mailpitANVILBASE_MAILPIT_CPUS (1)ANVILBASE_MAILPIT_MEM_LIMIT (512m)
consoleANVILBASE_CONSOLE_CPUS (1)ANVILBASE_CONSOLE_MEM_LIMIT (512m)
docker-socket-proxyANVILBASE_SOCKET_PROXY_CPUS (1)ANVILBASE_SOCKET_PROXY_MEM_LIMIT (256m)
wal-gANVILBASE_WALG_CPUS (2)ANVILBASE_WALG_MEM_LIMIT (2g)

PID caps (fork-bomb protection)

The deno runtime executes untrusted per-project edge-function code, so it also pins a hard PID cap (deploy.resources.limits.pids, the schema-correct form of pids_limit) — a PID cap bounds process/thread count and never OOMs:

ServicePID-limit var (default)
denoANVILBASE_DENO_PIDS_LIMIT (512)
control-planeANVILBASE_CONTROL_PLANE_PIDS_LIMIT (2048)
webhooksANVILBASE_WEBHOOKS_PIDS_LIMIT (2048)

These limits live in docker-compose.yml (the canonical local-=-cloud stack). The production overlay (docker-compose.prod.yml) is a separate concern and does not currently re-declare them; layer your own there if you need prod-specific ceilings.

Local development overrides

Create docker-compose.override.yml (git-ignored, auto-merged) for local tweaks — debug logging, exposed ports, source mounts for hot reload:

services:
control-plane:
environment:
- RUST_LOG=debug
console:
volumes:
- ./console/src:/app/src

Hygiene

  • Never commit .env or secret values.
  • Store production secrets in a secrets manager (Vault, AWS/GCP Secrets Manager) and inject them.
  • Rotate on a schedule — application keys via rotation endpoints, database passwords via the hardening procedure.

Full catalog: Reference → Configuration.

Next: Production Checklist.