# briven data-plane stack — Phase 6 of BACKEND_FORK_BRIEF.md. # # Per-project Postgres + REST + Auth + Realtime + Schema-introspection, # fronted by Caddy. Studio (apps/studio) is deployed separately as a # Next.js app at studio.briven.tech and talks to this stack via the # Caddy proxy. # # Services dropped from upstream supabase docker-compose: # - studio (lives in apps/studio, deployed as separate Next app) # - kong (replaced with caddy — see plan §1) # - storage / imgproxy (deferred to Phase 9 per brief §5) # - functions (deferred — briven has separate Deno isolate plan) # - analytics (logflare) — briven uses Loki+Grafana instead # - vector (hard-rule violation of docs/DOCKER.md rule 6 — # bind-mounts /var/run/docker.sock; never re-add) # - supavisor (deferred to Phase 9 — pool sizing only matters at scale) # # Hard rules enforced (docs/DOCKER.md): # - every service uses logging: *briven-logging (cap 10MB x 3 files) # - no service mounts /var/run/docker.sock # - no auto-poll of the Docker API # - restart: unless-stopped, not always x-logging: &briven-logging driver: json-file options: max-size: '10m' max-file: '3' services: # ── Reverse proxy ──────────────────────────────────────────────────── # Strips identifying upstream headers (Server, X-Powered-By) before # any response leaves the trust zone. Adds Server: briven. Auto-TLS # via Let's Encrypt if BRIVEN_PUBLIC_HOST is set to a routable domain. caddy: image: caddy:2.10-alpine container_name: briven-caddy restart: unless-stopped logging: *briven-logging networks: - briven-network ports: - '${BRIVEN_PROXY_HTTP_PORT:-8000}:80' - '${BRIVEN_PROXY_HTTPS_PORT:-8443}:443' environment: BRIVEN_PUBLIC_HOST: ${BRIVEN_PUBLIC_HOST:-api.briven.tech} volumes: - ./volumes/proxy/caddy/Caddyfile:/etc/caddy/Caddyfile:ro,z - caddy_data:/data - caddy_config:/config depends_on: auth: condition: service_healthy rest: condition: service_healthy healthcheck: test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost/'] interval: 10s timeout: 5s retries: 5 # ── Postgres ───────────────────────────────────────────────────────── # The data plane. Schemas per project are created on demand by the # control-plane provisioning flow (Phase 7). db: image: supabase/postgres:15.8.1.085 container_name: briven-db restart: unless-stopped logging: *briven-logging networks: - briven-network ports: - '${BRIVEN_DB_HOST_PORT:-5432}:5432' environment: POSTGRES_HOST: /var/run/postgresql POSTGRES_PORT: 5432 POSTGRES_DB: ${BRIVEN_POSTGRES_DB:-postgres} POSTGRES_PASSWORD: ${BRIVEN_POSTGRES_PASSWORD:?BRIVEN_POSTGRES_PASSWORD is required} PGPORT: 5432 PGPASSWORD: ${BRIVEN_POSTGRES_PASSWORD:?BRIVEN_POSTGRES_PASSWORD is required} PGDATABASE: ${BRIVEN_POSTGRES_DB:-postgres} JWT_SECRET: ${BRIVEN_JWT_SECRET:?BRIVEN_JWT_SECRET is required} JWT_EXP: ${BRIVEN_JWT_EXP:-3600} volumes: - briven-db-data:/var/lib/postgresql/data - briven-db-config:/etc/postgresql-custom - ./volumes/db/realtime.sql:/docker-entrypoint-initdb.d/migrations/99-realtime.sql:Z - ./volumes/db/webhooks.sql:/docker-entrypoint-initdb.d/init-scripts/98-webhooks.sql:Z - ./volumes/db/roles.sql:/docker-entrypoint-initdb.d/init-scripts/99-roles.sql:Z - ./volumes/db/jwt.sql:/docker-entrypoint-initdb.d/init-scripts/99-jwt.sql:Z - ./volumes/db/_briven.sql:/docker-entrypoint-initdb.d/migrations/97-_briven.sql:Z - ./volumes/db/logs.sql:/docker-entrypoint-initdb.d/migrations/99-logs.sql:Z healthcheck: test: ['CMD', 'pg_isready', '-U', 'postgres', '-h', 'localhost'] interval: 5s timeout: 5s retries: 10 # ── Auth (gotrue) ──────────────────────────────────────────────────── # GOTRUE_* env names are baked into the upstream Go binary; we wrap # them with BRIVEN_AUTH_* indirection at the compose layer. Caddy # strips response headers (Server, X-Powered-By, Via) — see # volumes/proxy/caddy/Caddyfile. Response BODY scrubbing of upstream # identifiers (gotrue error messages, postgrest hints) is not yet # implemented — vanilla caddy:2-alpine lacks the replace-response # module. Tracked in HANDOFF.md §"Carry-over gaps". auth: image: supabase/gotrue:v2.186.0 container_name: briven-auth restart: unless-stopped logging: *briven-logging networks: - briven-network depends_on: db: condition: service_healthy environment: GOTRUE_API_HOST: 0.0.0.0 GOTRUE_API_PORT: 9999 API_EXTERNAL_URL: ${BRIVEN_API_EXTERNAL_URL:?BRIVEN_API_EXTERNAL_URL is required} GOTRUE_DB_DRIVER: postgres GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:${BRIVEN_POSTGRES_PASSWORD}@db:5432/${BRIVEN_POSTGRES_DB:-postgres} GOTRUE_SITE_URL: ${BRIVEN_AUTH_SITE_URL:?BRIVEN_AUTH_SITE_URL is required} GOTRUE_URI_ALLOW_LIST: ${BRIVEN_AUTH_URI_ALLOW_LIST:-} GOTRUE_DISABLE_SIGNUP: ${BRIVEN_AUTH_DISABLE_SIGNUP:-false} GOTRUE_JWT_ADMIN_ROLES: service_role GOTRUE_JWT_AUD: authenticated GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated GOTRUE_JWT_EXP: ${BRIVEN_JWT_EXP:-3600} GOTRUE_JWT_SECRET: ${BRIVEN_JWT_SECRET} GOTRUE_EXTERNAL_EMAIL_ENABLED: ${BRIVEN_AUTH_EMAIL_ENABLED:-true} GOTRUE_MAILER_AUTOCONFIRM: ${BRIVEN_AUTH_MAILER_AUTOCONFIRM:-false} GOTRUE_SMTP_HOST: ${BRIVEN_AUTH_SMTP_HOST:-} GOTRUE_SMTP_PORT: ${BRIVEN_AUTH_SMTP_PORT:-587} GOTRUE_SMTP_USER: ${BRIVEN_AUTH_SMTP_USER:-} GOTRUE_SMTP_PASS: ${BRIVEN_AUTH_SMTP_PASS:-} GOTRUE_SMTP_ADMIN_EMAIL: ${BRIVEN_AUTH_SMTP_ADMIN_EMAIL:-} GOTRUE_SMTP_SENDER_NAME: ${BRIVEN_AUTH_SMTP_SENDER_NAME:-briven} healthcheck: test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost:9999/health'] interval: 5s timeout: 5s retries: 10 # ── PostgREST ──────────────────────────────────────────────────────── # Auto-generates a REST API from the DB schema. PGRST_* names are # native; wrapped via BRIVEN_REST_* at the compose layer. rest: image: postgrest/postgrest:v14.8 container_name: briven-rest restart: unless-stopped logging: *briven-logging networks: - briven-network depends_on: db: condition: service_healthy environment: PGRST_DB_URI: postgres://authenticator:${BRIVEN_POSTGRES_PASSWORD}@db:5432/${BRIVEN_POSTGRES_DB:-postgres} PGRST_DB_SCHEMAS: ${BRIVEN_REST_DB_SCHEMAS:-public,storage,graphql_public} PGRST_DB_ANON_ROLE: anon PGRST_JWT_SECRET: ${BRIVEN_JWT_SECRET} PGRST_DB_USE_LEGACY_GUCS: 'false' PGRST_APP_SETTINGS_JWT_SECRET: ${BRIVEN_JWT_SECRET} PGRST_APP_SETTINGS_JWT_EXP: ${BRIVEN_JWT_EXP:-3600} command: - 'postgrest' healthcheck: test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost:3000/'] interval: 5s timeout: 5s retries: 10 # ── Realtime (Phoenix) ─────────────────────────────────────────────── # WebSocket subscriptions on Postgres logical replication. Native env # names (API_JWT_SECRET, DB_HOST, etc.) wrapped via BRIVEN_REALTIME_*. realtime: image: supabase/realtime:v2.76.5 container_name: briven-realtime restart: unless-stopped logging: *briven-logging networks: - briven-network depends_on: db: condition: service_healthy environment: PORT: 4000 DB_HOST: db DB_PORT: 5432 DB_USER: supabase_admin DB_PASSWORD: ${BRIVEN_POSTGRES_PASSWORD} DB_NAME: ${BRIVEN_POSTGRES_DB:-postgres} DB_AFTER_CONNECT_QUERY: 'SET search_path TO _realtime' DB_ENC_KEY: ${BRIVEN_REALTIME_ENC_KEY:?BRIVEN_REALTIME_ENC_KEY is required} API_JWT_SECRET: ${BRIVEN_JWT_SECRET} SECRET_KEY_BASE: ${BRIVEN_REALTIME_SECRET_KEY_BASE:?BRIVEN_REALTIME_SECRET_KEY_BASE is required} ERL_AFLAGS: -proto_dist inet_tcp DNS_NODES: "''" RLIMIT_NOFILE: '10000' APP_NAME: realtime SEED_SELF_HOST: 'true' RUN_JANITOR: 'true' healthcheck: test: - CMD-SHELL - 'curl -sSfL --head -o /dev/null -H "Authorization: Bearer ${BRIVEN_JWT_SECRET}" http://localhost:4000/api/tenants/realtime-dev/health' interval: 5s timeout: 5s retries: 3 # ── postgres-meta ──────────────────────────────────────────────────── # Schema introspection backend that Studio reads from. PG_META_* native; # wrapped via BRIVEN_META_*. meta: image: supabase/postgres-meta:v0.96.3 container_name: briven-meta restart: unless-stopped logging: *briven-logging networks: - briven-network depends_on: db: condition: service_healthy environment: PG_META_PORT: 8080 PG_META_DB_HOST: db PG_META_DB_PORT: 5432 PG_META_DB_NAME: ${BRIVEN_POSTGRES_DB:-postgres} PG_META_DB_USER: supabase_admin PG_META_DB_PASSWORD: ${BRIVEN_POSTGRES_PASSWORD} healthcheck: test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost:8080/health'] interval: 10s timeout: 5s retries: 5 # ── pgBackRest ─────────────────────────────────────────────────────── # Phase 9 of BACKEND_FORK_BRIEF.md. Continuous WAL archive + incremental # backups to Cloudflare R2 every 15 minutes; full backup nightly. Reads # PGDATA via a shared bind-mount with the db service (NOT via the # daemon API). Restore procedure documented in infra/datapane/RESTORE.md. pgbackrest: image: pgbackrest/pgbackrest:2.55.1 container_name: briven-pgbackrest restart: unless-stopped logging: *briven-logging networks: - briven-network depends_on: db: condition: service_healthy environment: PGBACKREST_REPO1_TYPE: s3 PGBACKREST_REPO1_S3_BUCKET: ${BRIVEN_BACKUP_R2_BUCKET:?BRIVEN_BACKUP_R2_BUCKET is required} PGBACKREST_REPO1_S3_ENDPOINT: ${BRIVEN_BACKUP_R2_ENDPOINT:?BRIVEN_BACKUP_R2_ENDPOINT is required} PGBACKREST_REPO1_S3_REGION: auto PGBACKREST_REPO1_S3_KEY: ${BRIVEN_BACKUP_R2_ACCESS_KEY:?BRIVEN_BACKUP_R2_ACCESS_KEY is required} PGBACKREST_REPO1_S3_KEY_SECRET: ${BRIVEN_BACKUP_R2_SECRET_KEY:?BRIVEN_BACKUP_R2_SECRET_KEY is required} PGBACKREST_REPO1_RETENTION_FULL: '4' PGBACKREST_REPO1_RETENTION_DIFF: '7' PGBACKREST_REPO1_CIPHER_TYPE: aes-256-cbc PGBACKREST_REPO1_CIPHER_PASS: ${BRIVEN_BACKUP_ENCRYPTION_KEY:?BRIVEN_BACKUP_ENCRYPTION_KEY is required} PGBACKREST_PROCESS_MAX: '2' PGBACKREST_COMPRESS_TYPE: zst PGBACKREST_LOG_LEVEL_FILE: info PGBACKREST_LOG_LEVEL_CONSOLE: warn volumes: - briven-db-data:/var/lib/postgresql/data:ro - briven-backup-cache:/var/lib/pgbackrest - ./pgbackrest/pgbackrest.conf:/etc/pgbackrest/pgbackrest.conf:ro,z - ./pgbackrest/crontab:/etc/crontabs/root:ro,z - ./pgbackrest/entrypoint.sh:/entrypoint.sh:ro,z entrypoint: ['/bin/sh'] command: ['/entrypoint.sh'] healthcheck: test: ['CMD', 'pgbackrest', '--stanza=briven', 'check'] interval: 1m timeout: 30s retries: 3 networks: briven-network: name: briven-network volumes: briven-db-data: name: briven-db-data briven-db-config: name: briven-db-config briven-backup-cache: name: briven-backup-cache caddy_data: name: briven-caddy-data caddy_config: name: briven-caddy-config