services: postgres: image: postgres:16-alpine container_name: vidreview-db environment: POSTGRES_USER: ${POSTGRES_USER:-vidreview} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Required} POSTGRES_DB: ${POSTGRES_DB:-vidreview} ports: - '5432:5432' volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ['CMD-SHELL', 'pg_isready -U vidreview'] interval: 5s timeout: 5s retries: 5 # ── Init: runs once on fresh deploy ───────────────────────────────────── # Creates admin account, locks registration, saves credentials to seed_output. # Skips silently if DB already has an admin (safe to re-run on updates). init: build: context: . dockerfile: Dockerfile.api container_name: vidreview-init entrypoint: ['bash', '/scripts/init-admin.sh'] environment: DB_HOST: postgres DB_NAME: ${POSTGRES_DB:-vidreview} DB_USER: ${POSTGRES_USER:-vidreview} DB_PASS: ${POSTGRES_PASSWORD:?Required} API_CONTAINER: vidreview-api OUTPUT_DIR: /seed-output ADMIN_EMAIL: ${ADMIN_EMAIL:-admin@vidreview.local} ADMIN_NAME: ${ADMIN_NAME:-Admin} volumes: - /var/run/docker.sock:/var/run/docker.sock:rw - /usr/bin/docker:/usr/bin/docker:rw - ./scripts:/scripts - seed_output:/seed-output depends_on: postgres: condition: service_healthy api: condition: service_healthy restart: 'no' # ── API ───────────────────────────────────────────────────────────────── api: build: context: . dockerfile: Dockerfile.api container_name: vidreview-api environment: DATABASE_URL: ${DATABASE_URL:?Required} JWT_SECRET: ${JWT_SECRET:?Required} JWT_EXPIRES_IN: ${JWT_EXPIRES_IN:-7d} API_PORT: 3001 NODE_ENV: ${NODE_ENV:-production} UPLOAD_DIR: /app/uploads MAX_FILE_SIZE_MB: ${MAX_FILE_SIZE_MB:-500} ALLOWED_ORIGINS: ${ALLOWED_ORIGINS} FRONTEND_URL: ${FRONTEND_URL} RESEND_API_KEY: ${RESEND_API_KEY:-} ports: - '3001:3001' depends_on: postgres: condition: service_healthy volumes: - uploads:/app/uploads healthcheck: test: ['CMD-SHELL', 'wget -qO- http://localhost:3001/health || exit 1'] interval: 10s timeout: 5s retries: 5 # ── Transcode Worker ───────────────────────────────────────────────────── worker: build: context: . dockerfile: Dockerfile.api container_name: vidreview-worker command: node src/worker/index.js environment: DATABASE_URL: ${DATABASE_URL:?Required} NODE_ENV: ${NODE_ENV:-production} UPLOAD_DIR: /app/uploads POLL_INTERVAL_MS: ${POLL_INTERVAL_MS:-2000} depends_on: postgres: condition: service_healthy volumes: - uploads:/app/uploads restart: unless-stopped # ── Caddy Reverse Proxy ────────────────────────────────────────────────── caddy: image: caddy:2-alpine container_name: vidreview-caddy ports: - '${CADDY_HTTP_PORT:-80}:80' - '${CADDY_HTTPS_PORT:-443}:443' volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config depends_on: - frontend - api # ── Frontend ──────────────────────────────────────────────────────────── frontend: build: context: . dockerfile: Dockerfile.frontend container_name: vidreview-frontend environment: NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL} NODE_ENV: ${NODE_ENV:-production} expose: - '3000' depends_on: api: condition: service_healthy volumes: postgres_data: uploads: seed_output: caddy_data: caddy_config: