| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- # 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
|