docker-compose.yml
Rama master · réplica de lectura del repositorio original.
# DOMINUS — Docker Compose for development and community deployment.
#
# Community edition (SQLite, single-user): all services start by default.
# docker compose up -d
#
# DOMINUS Cloud (PostgreSQL, multi-tenant): use the production overlay,
# which adds postgres, redis and the monitoring stack:
# docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
#
# Production (with .env file for secrets):
# docker compose --env-file .env up -d
#
# Service note:
# api + worker + scheduler start by default — the job-queue worker and
# the background scheduler are required for async runs, backups and
# renewal checks (ADR-0023). This file intentionally has no
# PostgreSQL/Redis: the community edition runs on SQLite at €0 infra
# cost. The production overlay adds the data stores and monitoring.
services:
api:
build:
context: .
dockerfile: Dockerfile
target: api
image: dominus-api:latest
ports:
# Bind to loopback by default: the API is unauthenticated on /
# health only, and exposing the app port on the host network
# without a reverse proxy is a firewall dependency the operator
# must opt into explicitly. Override with 0.0.0.0:${PORT}:3000
# when a LAN-exposed deployment is intended.
- "127.0.0.1:${PORT:-3000}:3000"
volumes:
# DATA_DIR lets the local Docker gate run against a throwaway database
# instead of the developer's ./data (a stale schema there makes the
# smoke fail for reasons that have nothing to do with the image).
- ${DATA_DIR:-./data}:/app/data
environment:
- NODE_ENV=${NODE_ENV:-production}
- DATABASE_PATH=/app/data/dominus.db
- DATABASE_URL=${DATABASE_URL:-}
- PORT=${PORT:-3000}
- HOST=0.0.0.0
- LOG_LEVEL=${LOG_LEVEL:-info}
- LOG_PRETTY=${LOG_PRETTY:-false}
- KEYWORD_DATA_PATH=${KEYWORD_DATA_PATH:-}
- COMPS_DATA_PATH=${COMPS_DATA_PATH:-}
- EUIPO_CLIENT_ID=${EUIPO_CLIENT_ID:-}
- EUIPO_CLIENT_SECRET=${EUIPO_CLIENT_SECRET:-}
- USPTO_SEARCH_URL=${USPTO_SEARCH_URL:-}
- EUIPO_AUTH_URL=${EUIPO_AUTH_URL:-}
- EUIPO_API_URL=${EUIPO_API_URL:-}
- TM_CACHE_TTL_DAYS=${TM_CACHE_TTL_DAYS:-7}
- SCORING_WEIGHTS_OVERRIDE=${SCORING_WEIGHTS_OVERRIDE:-}
- DNS_BULK_CONCURRENCY=${DNS_BULK_CONCURRENCY:-10}
- WHOIS_LOOKUP_TIMEOUT=${WHOIS_LOOKUP_TIMEOUT:-10000}
- BUY_MAX_ABSOLUTE_CAP=${BUY_MAX_ABSOLUTE_CAP:-0}
- TRUST_PROXY_DEPTH=${TRUST_PROXY_DEPTH:-0}
- FRONTEND_DIST_PATH=./dist/web
# Boot-required: HOST below is 0.0.0.0, so the API refuses to start
# without authentication (fail-closed guard in src/index.ts). Without
# this guard the api container crash-loops through the ~30 s Unbound
# readiness wait on every restart, which looks like a hang to an
# operator. Docker Compose resolves this :? guard and aborts `up -d`
# with the fix in the message. The message intentionally contains no
# "=" and no shell substitution: Compose parses list-form environment
# entries by splitting on the first "=", so either would corrupt it.
- API_KEYS=${API_KEYS:?API_KEYS is required. Generate one with openssl rand -hex 32, then export it as admin=<key>}
- DROP_SCORE_THRESHOLD=${DROP_SCORE_THRESHOLD:-25}
- DROP_RENEWAL_HORIZON_DAYS=${DROP_RENEWAL_HORIZON_DAYS:-60}
- WORKER_ENABLED=false
- SCHEDULER_ENABLED=false
- REDIS_URL=${REDIS_URL:-}
- PURCHASE_AUTO_APPROVAL=${PURCHASE_AUTO_APPROVAL:-never}
# Unbound via the docker-compose.unbound.yml overlay (ADR-0072/0075).
# The overlay provides the Unbound sidecar and injects
# DNS_UNBOUND_HOSTS=unbound:5300 plus DNS_UNBOUND_STRICT=true.
# Without the overlay, DNS_UNBOUND_STRICT falls back to its
# context-aware default (ADR-0076): community edition uses the
# Node.js fallback for non-Available verdicts, cloud stays strict.
# Fallback never serves Available verdicts (ADR-0002 conservatism).
- DNS_UNBOUND_ENABLED=true
- DNSSEC_MODE=strict
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
compress: "true"
restart: unless-stopped
worker:
build:
context: .
dockerfile: Dockerfile
target: worker
image: dominus-worker:latest
depends_on:
api:
condition: service_healthy
volumes:
- ${DATA_DIR:-./data}:/app/data
- backups:/backups
environment:
- NODE_ENV=${NODE_ENV:-production}
- DATABASE_PATH=/app/data/dominus.db
- DATABASE_URL=${DATABASE_URL:-}
- LOG_LEVEL=${LOG_LEVEL:-info}
- KEYWORD_DATA_PATH=${KEYWORD_DATA_PATH:-}
- COMPS_DATA_PATH=${COMPS_DATA_PATH:-}
- EUIPO_CLIENT_ID=${EUIPO_CLIENT_ID:-}
- EUIPO_CLIENT_SECRET=${EUIPO_CLIENT_SECRET:-}
- USPTO_SEARCH_URL=${USPTO_SEARCH_URL:-}
- EUIPO_AUTH_URL=${EUIPO_AUTH_URL:-}
- EUIPO_API_URL=${EUIPO_API_URL:-}
- TM_CACHE_TTL_DAYS=${TM_CACHE_TTL_DAYS:-7}
- SCORING_WEIGHTS_OVERRIDE=${SCORING_WEIGHTS_OVERRIDE:-}
- DNS_BULK_CONCURRENCY=${DNS_BULK_CONCURRENCY:-10}
- WHOIS_LOOKUP_TIMEOUT=${WHOIS_LOOKUP_TIMEOUT:-10000}
- BUY_MAX_ABSOLUTE_CAP=${BUY_MAX_ABSOLUTE_CAP:-0}
- DROP_SCORE_THRESHOLD=${DROP_SCORE_THRESHOLD:-25}
- DROP_RENEWAL_HORIZON_DAYS=${DROP_RENEWAL_HORIZON_DAYS:-60}
- WORKER_ENABLED=true
- SCHEDULER_ENABLED=false
- WORKER_CONCURRENCY=${WORKER_CONCURRENCY:-2}
- JOB_QUEUE_POLL_INTERVAL_MS=${JOB_QUEUE_POLL_INTERVAL_MS:-1000}
- BACKUP_DIR=${BACKUP_DIR:-/backups}
- REDIS_URL=${REDIS_URL:-}
# Unbound via the docker-compose.unbound.yml overlay (ADR-0072/0075).
# The overlay provides the Unbound sidecar and injects
# DNS_UNBOUND_HOSTS=unbound:5300 plus DNS_UNBOUND_STRICT=true.
# Without the overlay, DNS_UNBOUND_STRICT falls back to its
# context-aware default (ADR-0076): community edition uses the
# Node.js fallback for non-Available verdicts, cloud stays strict.
# Fallback never serves Available verdicts (ADR-0002 conservatism).
- DNS_UNBOUND_ENABLED=true
- DNSSEC_MODE=strict
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
compress: "true"
restart: unless-stopped
scheduler:
build:
context: .
dockerfile: Dockerfile
target: scheduler
image: dominus-scheduler:latest
depends_on:
api:
condition: service_healthy
volumes:
- ${DATA_DIR:-./data}:/app/data
- backups:/backups
environment:
- NODE_ENV=${NODE_ENV:-production}
- DATABASE_PATH=/app/data/dominus.db
- DATABASE_URL=${DATABASE_URL:-}
- LOG_LEVEL=${LOG_LEVEL:-info}
- KEYWORD_DATA_PATH=${KEYWORD_DATA_PATH:-}
- COMPS_DATA_PATH=${COMPS_DATA_PATH:-}
- EUIPO_CLIENT_ID=${EUIPO_CLIENT_ID:-}
- EUIPO_CLIENT_SECRET=${EUIPO_CLIENT_SECRET:-}
- USPTO_SEARCH_URL=${USPTO_SEARCH_URL:-}
- EUIPO_AUTH_URL=${EUIPO_AUTH_URL:-}
- EUIPO_API_URL=${EUIPO_API_URL:-}
- TM_CACHE_TTL_DAYS=${TM_CACHE_TTL_DAYS:-7}
- SCORING_WEIGHTS_OVERRIDE=${SCORING_WEIGHTS_OVERRIDE:-}
- DNS_BULK_CONCURRENCY=${DNS_BULK_CONCURRENCY:-10}
- WHOIS_LOOKUP_TIMEOUT=${WHOIS_LOOKUP_TIMEOUT:-10000}
- BUY_MAX_ABSOLUTE_CAP=${BUY_MAX_ABSOLUTE_CAP:-0}
- DROP_SCORE_THRESHOLD=${DROP_SCORE_THRESHOLD:-25}
- DROP_RENEWAL_HORIZON_DAYS=${DROP_RENEWAL_HORIZON_DAYS:-60}
- WORKER_ENABLED=false
- SCHEDULER_ENABLED=true
- SCHEDULER_BACKUP_CRON=${SCHEDULER_BACKUP_CRON:-0 4 * * *}
- SCHEDULER_PRUNE_CRON=${SCHEDULER_PRUNE_CRON:-0 10 1 * *}
- BACKUP_DIR=${BACKUP_DIR:-/backups}
- REDIS_URL=${REDIS_URL:-}
# Unbound via the docker-compose.unbound.yml overlay (ADR-0072/0075).
# The overlay provides the Unbound sidecar and injects
# DNS_UNBOUND_HOSTS=unbound:5300 plus DNS_UNBOUND_STRICT=true.
# Without the overlay, DNS_UNBOUND_STRICT falls back to its
# context-aware default (ADR-0076): community edition uses the
# Node.js fallback for non-Available verdicts, cloud stays strict.
# Fallback never serves Available verdicts (ADR-0002 conservatism).
- DNS_UNBOUND_ENABLED=true
- DNSSEC_MODE=strict
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
compress: "true"
restart: unless-stopped
# Dedicated backups volume: keeps VACUUM INTO/pg_dump backups off the
# disk that holds dominus.db (isolation from DB write amplification and
# from accidental wipes of ./data). The image pre-creates /backups owned
# by the non-root dominus user. For off-disk durability, mount an
# external drive at /backups instead (see docs/deployment/README.md).
volumes:
backups: