services: caddy: image: caddy:2-alpine environment: DOMAIN: ${DOMAIN} BASIC_AUTH_USER: ${BASIC_AUTH_USER:-admin} BASIC_AUTH_HASH: ${BASIC_AUTH_HASH} ports: - "80:80" - "443:443" volumes: - ./ci/Caddyfile:/etc/caddy/Caddyfile:ro - caddy-data:/data - caddy-config:/config depends_on: server: condition: service_healthy client: condition: service_healthy restart: unless-stopped db: image: postgres:16-alpine environment: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB} volumes: - db-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] interval: 10s timeout: 5s retries: 3 restart: unless-stopped server: build: context: . dockerfile: .docker/server.Dockerfile target: production additional_contexts: project-q: ${PROJECT_Q_PATH} nest-service-locator: ${NEST_SERVICE_LOCATOR_PATH} environment: PORT: "3000" NODE_ENV: production DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} JWT_SECRET: ${JWT_SECRET} JWT_ACCESS_TOKEN_EXPIRY: ${JWT_ACCESS_TOKEN_EXPIRY:-15m} JWT_REFRESH_TOKEN_EXPIRY: ${JWT_REFRESH_TOKEN_EXPIRY:-7d} ENCRYPTION_KEY: ${ENCRYPTION_KEY} RESEND_API_KEY: ${RESEND_API_KEY:-} EMAIL_FROM_ADDRESS: ${EMAIL_FROM_ADDRESS:-} EMAIL_FROM_NAME: ${EMAIL_FROM_NAME:-CV Generator} CLIENT_URL: ${PUBLIC_URL} ALLOWED_ORIGINS: ${PUBLIC_URL} AI_PROVIDER: ${AI_PROVIDER:-anthropic} AI_TEMPERATURE: ${AI_TEMPERATURE:-0.1} AI_MAX_TOKENS: ${AI_MAX_TOKENS:-8192} AI_TIMEOUT: ${AI_TIMEOUT:-60000} ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-} ANTHROPIC_MODEL: ${ANTHROPIC_MODEL:-claude-sonnet-4-5-20250929} OPENAI_API_KEY: ${OPENAI_API_KEY:-} OPENAI_MODEL: ${OPENAI_MODEL:-gpt-4o-mini} PDF_OUTPUT_DIR: /app/pdf-output depends_on: db: condition: service_healthy volumes: - worker-output:/app/pdf-output:ro healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 15s timeout: 5s retries: 3 start_period: 30s restart: unless-stopped client: build: context: . dockerfile: .docker/client.Dockerfile target: production args: VITE_SERVER_URL: ${PUBLIC_URL} VITE_DOCS_URL: https://docs.${DOMAIN} healthcheck: test: ["CMD", "curl", "-f", "http://localhost:80"] interval: 15s timeout: 5s retries: 3 start_period: 5s restart: unless-stopped worker: build: context: . dockerfile: .docker/worker.Dockerfile target: production additional_contexts: project-q: ${PROJECT_Q_PATH} nest-service-locator: ${NEST_SERVICE_LOCATOR_PATH} environment: DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} QUEUE_SCHEMA: ${QUEUE_SCHEMA:-queue} QUEUE_NAME: ${QUEUE_NAME:-default} POLL_INTERVAL_MS: ${POLL_INTERVAL_MS:-2000} PDF_OUTPUT_DIR: /app/pdf-output PDF_TIMEOUT_MS: ${PDF_TIMEOUT_MS:-30000} HEARTBEAT_FILE_PATH: /tmp/worker-heartbeat HEARTBEAT_DB_INTERVAL_MS: ${HEARTBEAT_DB_INTERVAL_MS:-30000} CHROMIUM_ARGS: ${CHROMIUM_ARGS:---no-sandbox,--disable-dev-shm-usage,--disable-gpu,--single-process,--disable-extensions} depends_on: db: condition: service_healthy volumes: - worker-output:/app/pdf-output deploy: resources: limits: memory: 512M healthcheck: test: ["CMD-SHELL", "find /tmp/worker-heartbeat -mmin -1 | grep -q ."] interval: 30s timeout: 5s retries: 3 start_period: 15s restart: unless-stopped docs: profiles: - docs build: context: . dockerfile: .docker/docs.Dockerfile target: production args: VITE_CLIENT_URL: ${PUBLIC_URL} VITE_SERVER_URL: ${PUBLIC_URL} healthcheck: test: ["CMD", "curl", "-f", "http://localhost:80"] interval: 15s timeout: 5s retries: 3 start_period: 5s restart: unless-stopped volumes: db-data: worker-output: caddy-data: caddy-config: