232 lines
9.9 KiB
YAML
232 lines
9.9 KiB
YAML
# Staging compose overlay (reference only).
|
|
#
|
|
# Coolify on the hivegrid-staging host does NOT consume this file directly —
|
|
# each service is provisioned through the Coolify UI. This overlay is the
|
|
# human-readable source of truth for how the staging stack is meant to be
|
|
# wired (PgBouncer in front of Postgres, apps depending on it being healthy)
|
|
# and can be applied locally for end-to-end smoke tests:
|
|
#
|
|
# docker compose -f docker-compose.yml -f docker-compose.staging.yml up \
|
|
# postgres pgbouncer api web worker-general worker-mail worker-import worker-webhook worker-plugin
|
|
#
|
|
# See docs/ops/pgbouncer.md for the matching Coolify deploy procedure.
|
|
|
|
services:
|
|
postgres:
|
|
image: postgres:16-alpine@sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777
|
|
environment:
|
|
POSTGRES_DB: ${POSTGRES_DB:-hivegrid_validation}
|
|
POSTGRES_USER: ${POSTGRES_USER:-hivegrid}
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-hivegrid}
|
|
# Required so pg_authid stores SCRAM hashes that pgbouncer can copy
|
|
# into its userlist.txt. md5 would force a config-time rehash.
|
|
POSTGRES_INITDB_ARGS: "--auth-host=scram-sha-256 --auth-local=scram-sha-256"
|
|
command:
|
|
- postgres
|
|
- -c
|
|
- password_encryption=scram-sha-256
|
|
# Defensive max_connections so PgBouncer's default_pool_size (25)
|
|
# multiplied across app stacks stays well below the cap.
|
|
- -c
|
|
- max_connections=200
|
|
volumes:
|
|
- postgres-data:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-hivegrid} -d ${POSTGRES_DB:-hivegrid_validation}"]
|
|
interval: 10s
|
|
timeout: 3s
|
|
retries: 10
|
|
start_period: 20s
|
|
restart: unless-stopped
|
|
|
|
pgbouncer:
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
|
|
# Explicit one-shot authority. Run with:
|
|
# docker compose -f docker-compose.yml -f docker-compose.staging.yml --profile migrate run --rm migrator
|
|
# DIRECT_URL exists only in this short-lived container. Seeding is not part
|
|
# of the non-superuser migration authority.
|
|
migrator:
|
|
build:
|
|
context: .
|
|
target: web
|
|
entrypoint: ["./start.sh", "migrate"]
|
|
environment:
|
|
- DIRECT_URL=${MIGRATOR_DIRECT_URL:?MIGRATOR_DIRECT_URL is required}
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
profiles: ["migrate"]
|
|
restart: "no"
|
|
|
|
api:
|
|
build:
|
|
context: .
|
|
target: api
|
|
environment:
|
|
- DATABASE_URL=${APP_DATABASE_URL:?APP_DATABASE_URL is required}
|
|
- DATABASE_RUNTIME_ROLE=hivegrid_app
|
|
- RLS_RUNTIME_MODE=required
|
|
- REDIS_URL=${REDIS_URL:-redis://redis:6379/0}
|
|
- CHAT_GATEWAY_AUTH_SECRET=${CHAT_GATEWAY_AUTH_SECRET:?CHAT_GATEWAY_AUTH_SECRET is required}
|
|
- CLAMD_HOST=${CLAMD_HOST:-clamav}
|
|
- CLAMD_PORT=${CLAMD_PORT:-3310}
|
|
- CLAMD_TIMEOUT_MS=${CLAMD_TIMEOUT_MS:-30000}
|
|
- STORAGE_ENDPOINT=${STORAGE_ENDPOINT:-}
|
|
- STORAGE_REGION=${STORAGE_REGION:-}
|
|
- STORAGE_BUCKET=${STORAGE_BUCKET:-}
|
|
- STORAGE_ACCESS_KEY_ID=${STORAGE_ACCESS_KEY_ID:-}
|
|
- STORAGE_SECRET_ACCESS_KEY=${STORAGE_SECRET_ACCESS_KEY:-}
|
|
- STORAGE_SESSION_TOKEN=${STORAGE_SESSION_TOKEN:-}
|
|
- STORAGE_FORCE_PATH_STYLE=${STORAGE_FORCE_PATH_STYLE:-false}
|
|
depends_on:
|
|
pgbouncer:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_healthy
|
|
clamav:
|
|
condition: service_healthy
|
|
|
|
web:
|
|
build:
|
|
context: .
|
|
target: web
|
|
environment:
|
|
- DATABASE_URL=${APP_DATABASE_URL:?APP_DATABASE_URL is required}
|
|
- DATABASE_RUNTIME_ROLE=hivegrid_app
|
|
- RLS_RUNTIME_MODE=required
|
|
# Dedicated callback-only capability. Never forward this DSN to API,
|
|
# Hocuspocus, workers, or the one-shot migrator.
|
|
- AUTHENTIK_ADMIN_JIT_DATABASE_URL=${AUTHENTIK_ADMIN_JIT_DATABASE_URL:-}
|
|
- REDIS_URL=${REDIS_URL:?REDIS_URL is required}
|
|
- BULLMQ_REDIS_URL=${BULLMQ_REDIS_URL:-redis://redis:6379/0}
|
|
- SCAN_QUEUE_ENABLED=${SCAN_QUEUE_ENABLED:-true}
|
|
- CLAMD_HOST=${CLAMD_HOST:-clamav}
|
|
- CLAMD_PORT=${CLAMD_PORT:-3310}
|
|
- CLAMD_TIMEOUT_MS=${CLAMD_TIMEOUT_MS:-30000}
|
|
- STORAGE_ENDPOINT=${STORAGE_ENDPOINT:?STORAGE_ENDPOINT is required}
|
|
- STORAGE_REGION=${STORAGE_REGION:?STORAGE_REGION is required}
|
|
- STORAGE_BUCKET=${STORAGE_BUCKET:?STORAGE_BUCKET is required}
|
|
- STORAGE_ACCESS_KEY_ID=${WEB_STORAGE_ACCESS_KEY_ID:?WEB_STORAGE_ACCESS_KEY_ID is required}
|
|
- STORAGE_SECRET_ACCESS_KEY=${WEB_STORAGE_SECRET_ACCESS_KEY:?WEB_STORAGE_SECRET_ACCESS_KEY is required}
|
|
- STORAGE_SESSION_TOKEN=${WEB_STORAGE_SESSION_TOKEN:-}
|
|
- STORAGE_FORCE_PATH_STYLE=${STORAGE_FORCE_PATH_STYLE:-false}
|
|
- GDPR_EXPORT_SSE_C_KEY_BASE64=${GDPR_EXPORT_SSE_C_KEY_BASE64:?GDPR_EXPORT_SSE_C_KEY_BASE64 is required}
|
|
- AIRCALL_CLIENT_ID=${AIRCALL_CLIENT_ID:-}
|
|
- AIRCALL_CLIENT_SECRET=${AIRCALL_CLIENT_SECRET:-}
|
|
- AIRCALL_REDIRECT_URI=${AIRCALL_REDIRECT_URI:-}
|
|
- AIRCALL_WEBHOOK_URL=${AIRCALL_WEBHOOK_URL:-}
|
|
- CHAT_GATEWAY_AUTH_SECRET=${CHAT_GATEWAY_AUTH_SECRET:?CHAT_GATEWAY_AUTH_SECRET is required}
|
|
- WEB_PUSH_VAPID_PUBLIC_KEY=${WEB_PUSH_VAPID_PUBLIC_KEY:?WEB_PUSH_VAPID_PUBLIC_KEY is required}
|
|
depends_on:
|
|
pgbouncer:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_healthy
|
|
clamav:
|
|
condition: service_healthy
|
|
|
|
# Five group-specific workers receive only the NOBYPASSRLS worker DSN.
|
|
# Migrator and seed credentials are intentionally absent.
|
|
worker-general:
|
|
environment:
|
|
- DATABASE_URL=${WORKER_DATABASE_URL:?WORKER_DATABASE_URL is required}
|
|
- DATABASE_RUNTIME_ROLE=hivegrid_worker
|
|
- RLS_RUNTIME_MODE=required
|
|
- SCAN_QUEUE_ENABLED=true
|
|
- CLAMAV_HOST=${CLAMAV_HOST:-clamav}
|
|
- CLAMAV_PORT=${CLAMAV_PORT:-3310}
|
|
- CLAMAV_TIMEOUT_MS=${CLAMAV_TIMEOUT_MS:-30000}
|
|
- STORAGE_ENDPOINT=${STORAGE_ENDPOINT:?STORAGE_ENDPOINT is required}
|
|
- STORAGE_REGION=${STORAGE_REGION:?STORAGE_REGION is required}
|
|
- STORAGE_BUCKET=${STORAGE_BUCKET:?STORAGE_BUCKET is required}
|
|
- STORAGE_ACCESS_KEY_ID=${WORKER_GENERAL_STORAGE_ACCESS_KEY_ID:?WORKER_GENERAL_STORAGE_ACCESS_KEY_ID is required}
|
|
- STORAGE_SECRET_ACCESS_KEY=${WORKER_GENERAL_STORAGE_SECRET_ACCESS_KEY:?WORKER_GENERAL_STORAGE_SECRET_ACCESS_KEY is required}
|
|
- STORAGE_SESSION_TOKEN=${WORKER_GENERAL_STORAGE_SESSION_TOKEN:-}
|
|
- STORAGE_FORCE_PATH_STYLE=${STORAGE_FORCE_PATH_STYLE:-false}
|
|
- STORAGE_QUARANTINE_BUCKET=${STORAGE_QUARANTINE_BUCKET:-}
|
|
- MEILI_URL=${MEILI_URL:?MEILI_URL is required}
|
|
- MEILI_API_KEY=${MEILI_API_KEY:?MEILI_API_KEY is required}
|
|
- EXPORT_RETENTION_QUEUE_ENABLED=true
|
|
- OUTBOX_DISPATCHER_ENABLED=true
|
|
- OUTBOX_DISPATCHER_REQUIRED=true
|
|
- LAGO_API_URL=${LAGO_API_URL:?LAGO_API_URL is required}
|
|
- LAGO_API_KEY=${LAGO_API_KEY:?LAGO_API_KEY is required}
|
|
- LAGO_TIMEOUT_MS=${LAGO_TIMEOUT_MS:-10000}
|
|
- MOLLIE_API_KEY=${MOLLIE_API_KEY:?MOLLIE_API_KEY is required}
|
|
- MOLLIE_PROFILE_ID=${MOLLIE_PROFILE_ID:-}
|
|
- MOLLIE_TIMEOUT_MS=${MOLLIE_TIMEOUT_MS:-10000}
|
|
- BILLING_MOLLIE_WEBHOOK_URL=${BILLING_MOLLIE_WEBHOOK_URL:?BILLING_MOLLIE_WEBHOOK_URL is required}
|
|
- WORKFLOW_TRIGGER_OUTBOX_ENABLED=true
|
|
- WORKFLOW_TRIGGER_OUTBOX_REQUIRED=true
|
|
- KWP_HIVEGRID_TENANT_ID=${KWP_HIVEGRID_TENANT_ID:-}
|
|
depends_on:
|
|
pgbouncer:
|
|
condition: service_healthy
|
|
|
|
worker-mail:
|
|
environment:
|
|
- DATABASE_URL=${WORKER_DATABASE_URL:?WORKER_DATABASE_URL is required}
|
|
- DATABASE_RUNTIME_ROLE=hivegrid_worker
|
|
- RLS_RUNTIME_MODE=required
|
|
depends_on:
|
|
pgbouncer:
|
|
condition: service_healthy
|
|
|
|
worker-import:
|
|
environment:
|
|
- DATABASE_URL=${WORKER_DATABASE_URL:?WORKER_DATABASE_URL is required}
|
|
- DATABASE_RUNTIME_ROLE=hivegrid_worker
|
|
- RLS_RUNTIME_MODE=required
|
|
- IMPORT_QUEUE_ENABLED=false
|
|
- IMPORT_QUEUE_RUNTIME_READY=false
|
|
- PORTAL_GDPR_EXPORT_QUEUE_ENABLED=true
|
|
- STORAGE_ENDPOINT=${STORAGE_ENDPOINT:?STORAGE_ENDPOINT is required}
|
|
- STORAGE_REGION=${STORAGE_REGION:?STORAGE_REGION is required}
|
|
- STORAGE_BUCKET=${STORAGE_BUCKET:?STORAGE_BUCKET is required}
|
|
- STORAGE_ACCESS_KEY_ID=${WORKER_IMPORT_STORAGE_ACCESS_KEY_ID:?WORKER_IMPORT_STORAGE_ACCESS_KEY_ID is required}
|
|
- STORAGE_SECRET_ACCESS_KEY=${WORKER_IMPORT_STORAGE_SECRET_ACCESS_KEY:?WORKER_IMPORT_STORAGE_SECRET_ACCESS_KEY is required}
|
|
- STORAGE_SESSION_TOKEN=${WORKER_IMPORT_STORAGE_SESSION_TOKEN:-}
|
|
- STORAGE_FORCE_PATH_STYLE=${STORAGE_FORCE_PATH_STYLE:-false}
|
|
- GDPR_EXPORT_SSE_C_KEY_BASE64=${GDPR_EXPORT_SSE_C_KEY_BASE64:?GDPR_EXPORT_SSE_C_KEY_BASE64 is required}
|
|
- KWP_HIVEGRID_TENANT_ID=${KWP_HIVEGRID_TENANT_ID:-}
|
|
depends_on:
|
|
pgbouncer:
|
|
condition: service_healthy
|
|
|
|
worker-webhook:
|
|
environment:
|
|
- DATABASE_URL=${WORKER_DATABASE_URL:?WORKER_DATABASE_URL is required}
|
|
- DATABASE_RUNTIME_ROLE=hivegrid_worker
|
|
- RLS_RUNTIME_MODE=required
|
|
- WEB_PUSH_VAPID_SUBJECT=${WEB_PUSH_VAPID_SUBJECT:?WEB_PUSH_VAPID_SUBJECT is required}
|
|
- WEB_PUSH_VAPID_PUBLIC_KEY=${WEB_PUSH_VAPID_PUBLIC_KEY:?WEB_PUSH_VAPID_PUBLIC_KEY is required}
|
|
- WEB_PUSH_VAPID_PRIVATE_KEY=${WEB_PUSH_VAPID_PRIVATE_KEY:?WEB_PUSH_VAPID_PRIVATE_KEY is required}
|
|
depends_on:
|
|
pgbouncer:
|
|
condition: service_healthy
|
|
|
|
worker-plugin:
|
|
environment:
|
|
- DATABASE_URL=${WORKER_DATABASE_URL:?WORKER_DATABASE_URL is required}
|
|
- DATABASE_RUNTIME_ROLE=hivegrid_worker
|
|
- RLS_RUNTIME_MODE=required
|
|
depends_on:
|
|
pgbouncer:
|
|
condition: service_healthy
|
|
|
|
hocuspocus:
|
|
environment:
|
|
- NODE_ENV=production
|
|
- HOCUSPOCUS_PORT=1234
|
|
- HOCUSPOCUS_HOST=0.0.0.0
|
|
- HOCUSPOCUS_AUTH_SECRET=${HOCUSPOCUS_AUTH_SECRET:-}
|
|
- DATABASE_URL=${APP_DATABASE_URL:?APP_DATABASE_URL is required}
|
|
- DATABASE_RUNTIME_ROLE=hivegrid_app
|
|
- RLS_RUNTIME_MODE=required
|
|
|
|
volumes:
|
|
postgres-data:
|