# Base compose stack. Uses ${VAR:-default} interpolation throughout so the # stack boots with zero config — sane dev defaults baked in. For production # deployments, override the defaults via shell env vars or a .env file: # # DB_PASSWORD=...real... SECRET_KEY=...real... docker compose up # # The dev override (docker-compose.override.yml) is auto-merged when you # run `docker compose up` from this directory and switches images to # local builds + DEBUG logging. services: redis: image: redis:7-alpine volumes: - redis_data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 postgres: image: pgvector/pgvector:pg16 # Docker's default /dev/shm is 64MB; VACUUM (ANALYZE) and parallel queries # allocate larger shared-memory segments and fail with # "could not resize shared memory segment ... No space left on device" # (operator-flagged 2026-06-07, vacuum_analyze on import_task needed 67MB). shm_size: 512m environment: POSTGRES_USER: ${DB_USER:-fabledcurator} POSTGRES_PASSWORD: ${DB_PASSWORD:-fabledcurator_dev} POSTGRES_DB: ${DB_NAME:-fabledcurator} volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-fabledcurator}"] interval: 10s timeout: 5s retries: 5 web: image: git.fabledsword.com/bvandeusen/fabledcurator:dev command: ["web"] ports: - "${PORT:-8080}:8080" environment: &app_env DB_USER: ${DB_USER:-fabledcurator} DB_PASSWORD: ${DB_PASSWORD:-fabledcurator_dev} DB_HOST: postgres DB_PORT: "5432" DB_NAME: ${DB_NAME:-fabledcurator} CELERY_BROKER_URL: redis://redis:6379/0 CELERY_RESULT_BACKEND: redis://redis:6379/0 SECRET_KEY: ${SECRET_KEY:-dev_secret_key_not_for_production_change_me} EXTENSION_API_KEY: ${EXTENSION_API_KEY:-} LOG_LEVEL: ${LOG_LEVEL:-INFO} volumes: - ./images:/images - ./import:/import - ./downloads:/downloads # FC-5 legacy migration: bind-mount the host's ImageRepo images dir # under /import (FC's existing filesystem scan picks them up). Read-only # is sufficient — FC copies into /images during the scan. The worker + # scheduler services see the same /import via their own mounts below # because of /import volume reuse. Edit the host path to match your # install before running Settings → Maintenance → Legacy migration. # - /var/lib/imagerepo/images:/import/imagerepo:ro depends_on: postgres: { condition: service_healthy } redis: { condition: service_healthy } worker: image: git.fabledsword.com/bvandeusen/fabledcurator:dev command: ["worker"] environment: <<: *app_env CELERY_QUEUES: default,import,thumbnail,download CELERY_CONCURRENCY: "2" volumes: - ./images:/images - ./import:/import - ./downloads:/downloads depends_on: postgres: { condition: service_healthy } redis: { condition: service_healthy } scheduler: image: git.fabledsword.com/bvandeusen/fabledcurator:dev command: ["scheduler"] environment: <<: *app_env CELERY_QUEUES: maintenance,scan volumes: - ./images:/images - ./import:/import - ./downloads:/downloads depends_on: postgres: { condition: service_healthy } redis: { condition: service_healthy } # Dedicated lane for long one-shot maintenance (DB backups, library audits, # admin maintenance). Kept off the scheduler's quick `maintenance` lane so a # 30-min backup or a multi-chunk audit can never starve the 5-min recovery # sweeps / vacuum (operator-flagged 2026-06-07). One slot — these are heavy. maintenance-long: image: git.fabledsword.com/bvandeusen/fabledcurator:dev command: ["worker"] environment: <<: *app_env CELERY_QUEUES: maintenance_long CELERY_CONCURRENCY: "1" volumes: - ./images:/images - ./import:/import - ./downloads:/downloads depends_on: postgres: { condition: service_healthy } redis: { condition: service_healthy } ml-worker: image: git.fabledsword.com/bvandeusen/fabledcurator-ml:dev command: ["ml-worker"] environment: <<: *app_env volumes: - ./images:/images:ro - ./models:/models depends_on: postgres: { condition: service_healthy } redis: { condition: service_healthy } volumes: redis_data: postgres_data: