269 lines
10 KiB
YAML
269 lines
10 KiB
YAML
services:
|
|
redis:
|
|
image: redis:7-alpine@sha256:6ab0b6e7381779332f97b8ca76193e45b0756f38d4c0dcda72dbb3c32061ab99
|
|
ports:
|
|
- "127.0.0.1:6379:6379"
|
|
healthcheck:
|
|
test: ["CMD", "redis-cli", "ping"]
|
|
interval: 5s
|
|
timeout: 3s
|
|
retries: 10
|
|
|
|
pgbouncer:
|
|
# Connection pooler in front of Postgres for application traffic (DATABASE_URL).
|
|
# Apps point DATABASE_URL at pgbouncer:6432, DIRECT_URL stays on postgres:5432
|
|
# so Prisma migrate can keep prepared statements. See docs/ops/pgbouncer.md.
|
|
image: edoburu/pgbouncer:latest@sha256:4c1ca296ef525f108f5d3552cc337c0c09587cf8dae7f0067fd93349e47dc1cd
|
|
# No `ports:` mapping — pgbouncer must only be reachable inside the Docker
|
|
# network. Public-facing 6432 would expose the pool to the open internet.
|
|
volumes:
|
|
- ./infra/pgbouncer/pgbouncer.ini:/etc/pgbouncer/pgbouncer.ini:ro
|
|
- ./infra/pgbouncer/userlist.txt:/etc/pgbouncer/userlist.txt:ro
|
|
healthcheck:
|
|
# pg_isready is shipped inside the edoburu image; queries the pool listener
|
|
# without holding a server-side connection.
|
|
test: ["CMD", "pg_isready", "-h", "127.0.0.1", "-p", "6432", "-U", "pgb_admin"]
|
|
interval: 10s
|
|
timeout: 3s
|
|
retries: 5
|
|
start_period: 10s
|
|
restart: unless-stopped
|
|
|
|
clamav:
|
|
image: clamav/clamav:stable@sha256:7f5389ccaa2368c383fa80e167ccfe44348d71e685f926fce4755eed1757673a
|
|
volumes:
|
|
- clamav-db:/var/lib/clamav
|
|
ports:
|
|
- "127.0.0.1:3310:3310"
|
|
healthcheck:
|
|
# CLAMD_STARTUP_TIMEOUT only matters for the first boot — freshclam pulls
|
|
# ~250MB of signatures before clamd starts listening. Allow ~20 minutes
|
|
# for cold starts; after that the check passes in <2s.
|
|
test: ["CMD", "clamdscan", "--ping", "1"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 40
|
|
start_period: 1200s
|
|
restart: unless-stopped
|
|
|
|
# Worker is split into five runtime variants — same image, same code,
|
|
# different WORKER_QUEUE_GROUP env. See docs/ops/worker-queues.md.
|
|
# `worker` is kept as a YAML anchor so the five group services share a
|
|
# single build context. Only worker-general depends on ClamAV because it
|
|
# owns the file-scan queue; other worker groups override depends_on to avoid
|
|
# blocking on first-boot signature downloads.
|
|
worker-general: &worker-base
|
|
build:
|
|
context: .
|
|
target: worker
|
|
environment:
|
|
- WORKER_ENABLED=${WORKER_ENABLED:-false}
|
|
- WORKER_QUEUE_GROUP=general
|
|
- WORKER_CONCURRENCY_GENERAL=${WORKER_CONCURRENCY_GENERAL:-10}
|
|
- WORKER_HEALTH_PORT=9101
|
|
- WORKER_ADMIN_PORT=9201
|
|
- BULLMQ_REDIS_URL=${BULLMQ_REDIS_URL:-redis://redis:6379/0}
|
|
- DATABASE_URL=${DATABASE_URL:-}
|
|
- APP_URL=${APP_URL:-}
|
|
- OUTBOX_DISPATCHER_ENABLED=${OUTBOX_DISPATCHER_ENABLED:-false}
|
|
- OUTBOX_DISPATCHER_REQUIRED=${OUTBOX_DISPATCHER_REQUIRED:-false}
|
|
- OUTBOX_DISPATCH_CONCURRENCY=${OUTBOX_DISPATCH_CONCURRENCY:-10}
|
|
- LAGO_API_URL=${LAGO_API_URL:-}
|
|
- LAGO_API_KEY=${LAGO_API_KEY:-}
|
|
- LAGO_TIMEOUT_MS=${LAGO_TIMEOUT_MS:-10000}
|
|
- MOLLIE_API_KEY=${MOLLIE_API_KEY:-}
|
|
- MOLLIE_PROFILE_ID=${MOLLIE_PROFILE_ID:-}
|
|
- MOLLIE_TIMEOUT_MS=${MOLLIE_TIMEOUT_MS:-10000}
|
|
- BILLING_MOLLIE_WEBHOOK_URL=${BILLING_MOLLIE_WEBHOOK_URL:-}
|
|
- WORKFLOW_TRIGGER_EXECUTOR_MODE=${WORKFLOW_TRIGGER_EXECUTOR_MODE:-http}
|
|
- WORKFLOW_TRIGGER_EXECUTOR_URL=${WORKFLOW_TRIGGER_EXECUTOR_URL:-}
|
|
- API_ACCESS_TOKEN=${API_ACCESS_TOKEN:-}
|
|
- WORKFLOW_TRIGGER_EXECUTOR_TIMEOUT_MS=${WORKFLOW_TRIGGER_EXECUTOR_TIMEOUT_MS:-30000}
|
|
- WORKFLOW_TRIGGER_OUTBOX_ENABLED=${WORKFLOW_TRIGGER_OUTBOX_ENABLED:-true}
|
|
- WORKFLOW_TRIGGER_OUTBOX_CONCURRENCY=${WORKFLOW_TRIGGER_OUTBOX_CONCURRENCY:-5}
|
|
- WORKFLOW_TRIGGER_OUTBOX_REQUIRED=${WORKFLOW_TRIGGER_OUTBOX_REQUIRED:-false}
|
|
- SCAN_QUEUE_ENABLED=${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_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}
|
|
- STORAGE_QUARANTINE_BUCKET=${STORAGE_QUARANTINE_BUCKET:-}
|
|
- MEILI_URL=${MEILI_URL:-}
|
|
- MEILI_API_KEY=${MEILI_API_KEY:-}
|
|
- EXPORT_RETENTION_QUEUE_ENABLED=${EXPORT_RETENTION_QUEUE_ENABLED:-false}
|
|
- KWP_HIVEGRID_TENANT_ID=${KWP_HIVEGRID_TENANT_ID:-}
|
|
depends_on:
|
|
redis:
|
|
condition: service_healthy
|
|
clamav:
|
|
condition: service_healthy
|
|
healthcheck:
|
|
test: ["CMD", "node", "apps/worker/dist/health.js"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 20s
|
|
|
|
worker-mail:
|
|
<<: *worker-base
|
|
environment:
|
|
- WORKER_ENABLED=${WORKER_ENABLED:-false}
|
|
- WORKER_QUEUE_GROUP=mail
|
|
- WORKER_CONCURRENCY_MAIL=${WORKER_CONCURRENCY_MAIL:-5}
|
|
- WORKER_HEALTH_PORT=9102
|
|
- WORKER_ADMIN_PORT=9202
|
|
- BULLMQ_REDIS_URL=${BULLMQ_REDIS_URL:-redis://redis:6379/0}
|
|
- DATABASE_URL=${DATABASE_URL:-}
|
|
- CRYPTO_SECRET=${CRYPTO_SECRET:-}
|
|
depends_on:
|
|
redis:
|
|
condition: service_healthy
|
|
healthcheck:
|
|
test: ["CMD", "node", "apps/worker/dist/health.js"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 20s
|
|
|
|
worker-import:
|
|
<<: *worker-base
|
|
environment:
|
|
- WORKER_ENABLED=${WORKER_ENABLED:-false}
|
|
- WORKER_QUEUE_GROUP=import
|
|
- WORKER_CONCURRENCY_IMPORT=${WORKER_CONCURRENCY_IMPORT:-1}
|
|
- WORKER_HEALTH_PORT=9103
|
|
- WORKER_ADMIN_PORT=9203
|
|
- BULLMQ_REDIS_URL=${BULLMQ_REDIS_URL:-redis://redis:6379/0}
|
|
- DATABASE_URL=${DATABASE_URL:-}
|
|
- IMPORT_QUEUE_ENABLED=${IMPORT_QUEUE_ENABLED:-false}
|
|
- IMPORT_QUEUE_RUNTIME_READY=${IMPORT_QUEUE_RUNTIME_READY:-false}
|
|
- PORTAL_GDPR_EXPORT_QUEUE_ENABLED=${PORTAL_GDPR_EXPORT_QUEUE_ENABLED:-false}
|
|
- 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}
|
|
- GDPR_EXPORT_SSE_C_KEY_BASE64=${GDPR_EXPORT_SSE_C_KEY_BASE64:-}
|
|
- KWP_HIVEGRID_TENANT_ID=${KWP_HIVEGRID_TENANT_ID:-}
|
|
depends_on:
|
|
redis:
|
|
condition: service_healthy
|
|
healthcheck:
|
|
test: ["CMD", "node", "apps/worker/dist/health.js"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 20s
|
|
|
|
worker-webhook:
|
|
<<: *worker-base
|
|
environment:
|
|
- WORKER_ENABLED=${WORKER_ENABLED:-false}
|
|
- WORKER_QUEUE_GROUP=webhook
|
|
- WORKER_CONCURRENCY_WEBHOOK=${WORKER_CONCURRENCY_WEBHOOK:-20}
|
|
- WORKER_HEALTH_PORT=9104
|
|
- WORKER_ADMIN_PORT=9204
|
|
- BULLMQ_REDIS_URL=${BULLMQ_REDIS_URL:-redis://redis:6379/0}
|
|
- DATABASE_URL=${DATABASE_URL:-}
|
|
- WEB_PUSH_VAPID_SUBJECT=${WEB_PUSH_VAPID_SUBJECT:-}
|
|
- WEB_PUSH_VAPID_PUBLIC_KEY=${WEB_PUSH_VAPID_PUBLIC_KEY:-}
|
|
- WEB_PUSH_VAPID_PRIVATE_KEY=${WEB_PUSH_VAPID_PRIVATE_KEY:-}
|
|
depends_on:
|
|
redis:
|
|
condition: service_healthy
|
|
healthcheck:
|
|
test: ["CMD", "node", "apps/worker/dist/health.js"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 20s
|
|
|
|
worker-plugin:
|
|
<<: *worker-base
|
|
environment:
|
|
- WORKER_ENABLED=${WORKER_ENABLED:-false}
|
|
- WORKER_QUEUE_GROUP=plugin
|
|
- WORKER_CONCURRENCY_PLUGIN=${WORKER_CONCURRENCY_PLUGIN:-5}
|
|
- WORKER_HEALTH_PORT=9105
|
|
- WORKER_ADMIN_PORT=9205
|
|
- BULLMQ_REDIS_URL=${BULLMQ_REDIS_URL:-redis://redis:6379/0}
|
|
- DATABASE_URL=${DATABASE_URL:-}
|
|
depends_on:
|
|
redis:
|
|
condition: service_healthy
|
|
healthcheck:
|
|
test: ["CMD", "node", "apps/worker/dist/health.js"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 20s
|
|
|
|
# Meilisearch — full-text search engine consumed by packages/search.
|
|
# Indexing is driven asynchronously by the worker-general service via
|
|
# outbox events; tenant isolation is enforced inside the wrapper. The
|
|
# master key is dev-only (`devkey`); production must inject a rotated
|
|
# secret. See docs/deploy/meilisearch.md.
|
|
meilisearch:
|
|
image: getmeili/meilisearch:v1.8@sha256:bbdb723dbf83ae431ad5c10bf4970a517ca1fce1dcda9bdb99691576e963a6e5
|
|
environment:
|
|
- MEILI_MASTER_KEY=${MEILI_MASTER_KEY:-devkey}
|
|
- MEILI_ENV=${MEILI_ENV:-development}
|
|
- MEILI_NO_ANALYTICS=true
|
|
ports:
|
|
# Bind to loopback only: the default master key is the well-known `devkey`,
|
|
# so the search engine must never be reachable from the LAN/host network.
|
|
# Production injects a rotated MEILI_MASTER_KEY via its own compose/secrets.
|
|
- "127.0.0.1:7700:7700"
|
|
volumes:
|
|
- meilisearch-data:/meili_data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -q -O - http://127.0.0.1:7700/health | grep -q 'available'"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 6
|
|
start_period: 15s
|
|
restart: unless-stopped
|
|
|
|
# Hocuspocus — realtime-collaboration WebSocket server backing the
|
|
# Novel-Editor (Y.js CRDT). Auth is JWT-signed via HOCUSPOCUS_AUTH_SECRET
|
|
# (issued by the web app's collab-token endpoint). Disabled by default —
|
|
# set HOCUSPOCUS_ENABLED=true in `.env` to opt in. See
|
|
# docs/features/realtime-collab.md.
|
|
hocuspocus:
|
|
build:
|
|
context: .
|
|
dockerfile: apps/hocuspocus/Dockerfile
|
|
target: hocuspocus
|
|
environment:
|
|
- NODE_ENV=${NODE_ENV:-production}
|
|
- HOCUSPOCUS_PORT=1234
|
|
- HOCUSPOCUS_HOST=0.0.0.0
|
|
- HOCUSPOCUS_AUTH_SECRET=${HOCUSPOCUS_AUTH_SECRET:-}
|
|
- DATABASE_URL=${DATABASE_URL:-}
|
|
- DIRECT_URL=${DIRECT_URL:-}
|
|
ports:
|
|
- "1234:1234"
|
|
profiles:
|
|
- realtime
|
|
healthcheck:
|
|
test:
|
|
- "CMD-SHELL"
|
|
- 'node -e "require(''net'').connect(1234,''127.0.0.1'').on(''connect'',()=>process.exit(0)).on(''error'',()=>process.exit(1))"'
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 15s
|
|
restart: unless-stopped
|
|
|
|
volumes:
|
|
clamav-db:
|
|
meilisearch-data:
|