Docker & horizontal scaling
Compose layout, dedicated workers, and scaling the API.
Docker and horizontal scaling
Compose topology
docker-compose.yml runs:
- postgres — data
- redis-cache — LRU cache (volatile OK)
- redis-queue — BullMQ (
noeviction, AOF) - api — Hono HTTP with
DISABLE_BACKGROUND_WORKERS=trueso it does not compete with workers - worker —
workers-standalone.js: BullMQ consumers + repeatable maintenance jobs - web — Next.js
- caddy — TLS + reverse proxy
Start locally:
docker compose up -d postgres redis-cache redis-queue api worker web
Horizontal scale
- API: increase replicas in
docker-compose.prod.ymlunderapi.deploy.replicas(ensure shared Postgres/Redis and session affinity if you introduce sticky state). - Workers: increase
worker.deploy.replicascautiously—jobs must be safe for concurrent consumers (idempotent handlers, row locks where needed). - Redis: use separate instances or logical DBs for cache vs queue (already split in Compose) so eviction cannot drop queued jobs.
Secrets in production
Mount secrets as files and map them in Compose (secrets:) so they appear under /run/secrets/. The API config loader reads those paths—see apps/api/src/config.ts and the trust-security doc.
Failure matrix (symptoms → what to check)
| Symptom | Likely cause |
|---------|----------------|
| Emails never arrive; BullMQ jobs pile up | worker container not running, or REDIS_QUEUE_URL wrong; API must use DISABLE_BACKGROUND_WORKERS=true when a separate worker runs (avoid double consumers). |
| API returns 503 on /health | Postgres down, DATABASE_URL wrong, or migrations not applied. |
| Redis errors / flaky rate limits | redis-cache unreachable; check REDIS_URL. |
| Jobs silently disappear | Queue Redis configured with allkeys-lru eviction—use the dedicated redis-queue service (noeviction). |
| Duplicate job execution | Two processes both running BullMQ workers (e.g. API without DISABLE_BACKGROUND_WORKERS and worker service). |
| /metrics empty or errors | Prometheus scrape path blocked; Redis warming up—see API logs. |
After editing app.config.ts modules in Docker images, run pnpm generate:active-modules before build so the route snapshot matches the image.