234 lines
8.2 KiB
YAML
234 lines
8.2 KiB
YAML
services:
|
|
redis:
|
|
image: redis:7-alpine
|
|
ports:
|
|
- "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
|
|
# 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
|
|
volumes:
|
|
- clamav-db:/var/lib/clamav
|
|
ports:
|
|
- "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}
|
|
- 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:-false}
|
|
- CLAMAV_HOST=${CLAMAV_HOST:-clamav}
|
|
- CLAMAV_PORT=${CLAMAV_PORT:-3310}
|
|
- CLAMAV_TIMEOUT_MS=${CLAMAV_TIMEOUT_MS:-30000}
|
|
depends_on:
|
|
redis:
|
|
condition: service_healthy
|
|
clamav:
|
|
condition: service_healthy
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:9101/healthz || exit 1"]
|
|
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:-}
|
|
depends_on:
|
|
redis:
|
|
condition: service_healthy
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:9102/healthz || exit 1"]
|
|
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:-}
|
|
depends_on:
|
|
redis:
|
|
condition: service_healthy
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:9103/healthz || exit 1"]
|
|
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:-}
|
|
depends_on:
|
|
redis:
|
|
condition: service_healthy
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:9104/healthz || exit 1"]
|
|
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-SHELL", "wget -qO- http://127.0.0.1:9105/healthz || exit 1"]
|
|
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
|
|
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:
|