# ============================================================================= # Docker Compose Configuration for Accessible Video Processing Platform # ============================================================================= # Services: # - api: FastAPI + Gunicorn REST API # - worker: Celery worker for background processing (default,ingest,notify,render) # - tts-worker: Dedicated TTS worker (tts queue, concurrency=8) # - ffmpeg-worker: Dedicated FFmpeg worker (ffmpeg queue, concurrency=1) # - whisper-worker: Dedicated Whisper worker (whisper queue, concurrency=1) # - mongodb: MongoDB database # - redis: Redis for Celery broker and cache # ============================================================================= version: '3.8' services: # --------------------------------------------------------------------------- # MongoDB Database # --------------------------------------------------------------------------- mongodb: image: mongo:7.0 container_name: accessible-video-mongodb restart: unless-stopped command: ["mongod", "--config", "/etc/mongod.conf", "--quiet"] environment: MONGO_INITDB_DATABASE: ${MONGODB_DB:-accessible_video} volumes: - mongodb-data:/data/db - mongodb-config:/data/configdb - ./config/mongod.conf:/etc/mongod.conf:ro networks: - accessible-video-network healthcheck: # TCP port check avoids mongosh connection metadata spam in logs test: ["CMD-SHELL", "timeout 5 bash -c ' sh -c "chown -R app:app /shared-tmp && su app -c 'celery -A celery_worker worker -Q default,ingest,notify,render --loglevel=info --concurrency=${WORKER_CONCURRENCY:-8}'" depends_on: mongodb: condition: service_healthy redis: condition: service_healthy environment: # Shared temp directory for ffmpeg operations TMPDIR: /shared-tmp # App configuration APP_ENV: ${APP_ENV:-dev} # Cloud Run Service URLs (set these to enable Cloud Run autoscaling) # When set, CPU-intensive operations are offloaded to Cloud Run WHISPER_SERVICE_URL: ${WHISPER_SERVICE_URL:-} FFMPEG_SERVICE_URL: ${FFMPEG_SERVICE_URL:-} # Auth (required by Settings class even though worker doesn't use it) JWT_SECRET: ${JWT_SECRET} JWT_ALG: ${JWT_ALG:-HS256} JWT_ACCESS_TTL_MIN: ${JWT_ACCESS_TTL_MIN:-240} JWT_REFRESH_TTL_DAYS: ${JWT_REFRESH_TTL_DAYS:-7} COOKIE_DOMAIN: ${COOKIE_DOMAIN:-ai-sandbox.oliver.solutions} COOKIE_SECURE: ${COOKIE_SECURE:-true} COOKIE_SAMESITE: ${COOKIE_SAMESITE:-Lax} # Database MONGODB_URI: mongodb://mongodb:27017/${MONGODB_DB:-accessible_video} MONGODB_DB: ${MONGODB_DB:-accessible_video} # Redis REDIS_URL: redis://redis:6379/0 CELERY_BROKER_URL: redis://redis:6379/0 CELERY_RESULT_BACKEND: redis://redis:6379/0 # GCP GCP_PROJECT_ID: ${GCP_PROJECT_ID} GCS_BUCKET: ${GCS_BUCKET:-accessible-video} GOOGLE_APPLICATION_CREDENTIALS: /secrets/gcp-credentials.json # AI Services GEMINI_API_KEY: ${GEMINI_API_KEY} TRANSLATE_API_KEY: ${TRANSLATE_API_KEY:-} ELEVENLABS_API_KEY: ${ELEVENLABS_API_KEY:-} GOOGLE_TTS_CREDENTIALS: /secrets/gcp-credentials.json # Email SENDGRID_API_KEY: ${SENDGRID_API_KEY:-} EMAIL_FROM: ${EMAIL_FROM:-noreply@ai-sandbox.oliver.solutions} CLIENT_BASE_URL: ${CLIENT_BASE_URL:-https://ai-sandbox.oliver.solutions/video-accessibility} # Microsoft Authentication AZURE_CLIENT_ID: ${AZURE_CLIENT_ID:-} AZURE_AUTHORITY: ${AZURE_AUTHORITY:-} AZURE_REDIRECT_URI: ${AZURE_REDIRECT_URI:-} # CORS CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:6001,http://localhost:5173,http://localhost:3000} # Observability SENTRY_DSN: ${SENTRY_DSN:-} # AI Cost Tracker COST_TRACKER_BASE_URL: ${COST_TRACKER_BASE_URL:-} COST_TRACKER_API_KEY: ${COST_TRACKER_API_KEY:-} COST_TRACKER_SOURCE_APP: ${COST_TRACKER_SOURCE_APP:-video-accessibility} COST_TRACKER_ENABLED: ${COST_TRACKER_ENABLED:-true} volumes: - ./secrets:/secrets:ro - worker-logs:/app/logs - shared-tmp:/shared-tmp networks: - accessible-video-network logging: driver: "json-file" options: max-size: "10m" max-file: "3" # --------------------------------------------------------------------------- # TTS Worker - Dedicated worker for TTS synthesis (concurrency=8) # --------------------------------------------------------------------------- tts-worker: build: context: ./backend dockerfile: Dockerfile target: worker container_name: accessible-video-tts-worker restart: unless-stopped user: root command: > sh -c "chown -R app:app /shared-tmp 2>/dev/null || true && su app -c 'celery -A celery_worker worker -Q tts --loglevel=info --concurrency=8'" depends_on: mongodb: condition: service_healthy redis: condition: service_healthy environment: # Shared temp directory TMPDIR: /shared-tmp # App configuration APP_ENV: ${APP_ENV:-dev} # Auth (required by Settings class even though worker doesn't use it) JWT_SECRET: ${JWT_SECRET} JWT_ALG: ${JWT_ALG:-HS256} JWT_ACCESS_TTL_MIN: ${JWT_ACCESS_TTL_MIN:-240} JWT_REFRESH_TTL_DAYS: ${JWT_REFRESH_TTL_DAYS:-7} COOKIE_DOMAIN: ${COOKIE_DOMAIN:-ai-sandbox.oliver.solutions} COOKIE_SECURE: ${COOKIE_SECURE:-true} COOKIE_SAMESITE: ${COOKIE_SAMESITE:-Lax} # Database MONGODB_URI: mongodb://mongodb:27017/${MONGODB_DB:-accessible_video} MONGODB_DB: ${MONGODB_DB:-accessible_video} # Redis REDIS_URL: redis://redis:6379/0 CELERY_BROKER_URL: redis://redis:6379/0 CELERY_RESULT_BACKEND: redis://redis:6379/0 # GCP GCP_PROJECT_ID: ${GCP_PROJECT_ID} GCS_BUCKET: ${GCS_BUCKET:-accessible-video} GOOGLE_APPLICATION_CREDENTIALS: /secrets/gcp-credentials.json # AI Services GEMINI_API_KEY: ${GEMINI_API_KEY} TRANSLATE_API_KEY: ${TRANSLATE_API_KEY:-} ELEVENLABS_API_KEY: ${ELEVENLABS_API_KEY:-} GOOGLE_TTS_CREDENTIALS: /secrets/gcp-credentials.json # Email SENDGRID_API_KEY: ${SENDGRID_API_KEY:-} EMAIL_FROM: ${EMAIL_FROM:-noreply@ai-sandbox.oliver.solutions} CLIENT_BASE_URL: ${CLIENT_BASE_URL:-https://ai-sandbox.oliver.solutions/video-accessibility} # Microsoft Authentication AZURE_CLIENT_ID: ${AZURE_CLIENT_ID:-} AZURE_AUTHORITY: ${AZURE_AUTHORITY:-} AZURE_REDIRECT_URI: ${AZURE_REDIRECT_URI:-} # CORS CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:6001,http://localhost:5173,http://localhost:3000} # Observability SENTRY_DSN: ${SENTRY_DSN:-} # AI Cost Tracker COST_TRACKER_BASE_URL: ${COST_TRACKER_BASE_URL:-} COST_TRACKER_API_KEY: ${COST_TRACKER_API_KEY:-} COST_TRACKER_SOURCE_APP: ${COST_TRACKER_SOURCE_APP:-video-accessibility} COST_TRACKER_ENABLED: ${COST_TRACKER_ENABLED:-true} volumes: - ./secrets:/secrets:ro - tts-worker-logs:/app/logs - shared-tmp:/shared-tmp networks: - accessible-video-network logging: driver: "json-file" options: max-size: "10m" max-file: "3" # --------------------------------------------------------------------------- # FFmpeg Worker - Dedicated worker for video encoding # Concurrency: 1 for local mode (CPU bound), 20 for Cloud Run mode (HTTP calls) # --------------------------------------------------------------------------- ffmpeg-worker: build: context: ./backend dockerfile: Dockerfile target: worker container_name: accessible-video-ffmpeg-worker restart: unless-stopped user: root command: > sh -c "chown -R app:app /shared-tmp 2>/dev/null || true && su app -c 'celery -A celery_worker worker -Q ffmpeg --loglevel=info --concurrency=${FFMPEG_WORKER_CONCURRENCY:-1}'" depends_on: mongodb: condition: service_healthy redis: condition: service_healthy environment: # Shared temp directory for ffmpeg operations TMPDIR: /shared-tmp # App configuration APP_ENV: ${APP_ENV:-dev} # Cloud Run Service URLs (set these to enable Cloud Run autoscaling) # When set, FFmpeg operations are offloaded to Cloud Run (HTTP calls) # This allows higher concurrency since workers just wait for HTTP responses FFMPEG_SERVICE_URL: ${FFMPEG_SERVICE_URL:-} # Auth (required by Settings class even though worker doesn't use it) JWT_SECRET: ${JWT_SECRET} JWT_ALG: ${JWT_ALG:-HS256} JWT_ACCESS_TTL_MIN: ${JWT_ACCESS_TTL_MIN:-240} JWT_REFRESH_TTL_DAYS: ${JWT_REFRESH_TTL_DAYS:-7} COOKIE_DOMAIN: ${COOKIE_DOMAIN:-ai-sandbox.oliver.solutions} COOKIE_SECURE: ${COOKIE_SECURE:-true} COOKIE_SAMESITE: ${COOKIE_SAMESITE:-Lax} # Database MONGODB_URI: mongodb://mongodb:27017/${MONGODB_DB:-accessible_video} MONGODB_DB: ${MONGODB_DB:-accessible_video} # Redis REDIS_URL: redis://redis:6379/0 CELERY_BROKER_URL: redis://redis:6379/0 CELERY_RESULT_BACKEND: redis://redis:6379/0 # GCP GCP_PROJECT_ID: ${GCP_PROJECT_ID} GCS_BUCKET: ${GCS_BUCKET:-accessible-video} GOOGLE_APPLICATION_CREDENTIALS: /secrets/gcp-credentials.json # AI Services GEMINI_API_KEY: ${GEMINI_API_KEY} TRANSLATE_API_KEY: ${TRANSLATE_API_KEY:-} ELEVENLABS_API_KEY: ${ELEVENLABS_API_KEY:-} GOOGLE_TTS_CREDENTIALS: /secrets/gcp-credentials.json # Email SENDGRID_API_KEY: ${SENDGRID_API_KEY:-} EMAIL_FROM: ${EMAIL_FROM:-noreply@ai-sandbox.oliver.solutions} CLIENT_BASE_URL: ${CLIENT_BASE_URL:-https://ai-sandbox.oliver.solutions/video-accessibility} # Microsoft Authentication AZURE_CLIENT_ID: ${AZURE_CLIENT_ID:-} AZURE_AUTHORITY: ${AZURE_AUTHORITY:-} AZURE_REDIRECT_URI: ${AZURE_REDIRECT_URI:-} # CORS CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:6001,http://localhost:5173,http://localhost:3000} # Observability SENTRY_DSN: ${SENTRY_DSN:-} # AI Cost Tracker COST_TRACKER_BASE_URL: ${COST_TRACKER_BASE_URL:-} COST_TRACKER_API_KEY: ${COST_TRACKER_API_KEY:-} COST_TRACKER_SOURCE_APP: ${COST_TRACKER_SOURCE_APP:-video-accessibility} COST_TRACKER_ENABLED: ${COST_TRACKER_ENABLED:-true} volumes: - ./secrets:/secrets:ro - ffmpeg-worker-logs:/app/logs - shared-tmp:/shared-tmp networks: - accessible-video-network logging: driver: "json-file" options: max-size: "10m" max-file: "3" # --------------------------------------------------------------------------- # Whisper Worker - Dedicated worker for Whisper transcription # Concurrency: 1 for local mode (RAM bound ~4-6GB), 10 for Cloud Run mode (HTTP calls) # Used for refining AD pause points with word-level speech analysis # --------------------------------------------------------------------------- whisper-worker: build: context: ./backend dockerfile: Dockerfile target: whisper-worker container_name: accessible-video-whisper-worker restart: unless-stopped user: root command: > sh -c "chown -R app:app /shared-tmp 2>/dev/null || true && su app -c 'celery -A celery_worker worker -Q whisper --loglevel=info --concurrency=${WHISPER_WORKER_CONCURRENCY:-1} --max-tasks-per-child=50'" depends_on: mongodb: condition: service_healthy redis: condition: service_healthy environment: # Shared temp directory for audio processing TMPDIR: /shared-tmp # App configuration APP_ENV: ${APP_ENV:-dev} # Cloud Run Service URL (set to enable Cloud Run autoscaling) # When set, Whisper transcription is offloaded to Cloud Run (HTTP calls) # This allows higher concurrency since workers just wait for HTTP responses WHISPER_SERVICE_URL: ${WHISPER_SERVICE_URL:-} # Whisper Configuration (for local mode only) WHISPER_MODEL: ${WHISPER_MODEL:-medium} # Auth (required by Settings class even though worker doesn't use it) JWT_SECRET: ${JWT_SECRET} JWT_ALG: ${JWT_ALG:-HS256} JWT_ACCESS_TTL_MIN: ${JWT_ACCESS_TTL_MIN:-240} JWT_REFRESH_TTL_DAYS: ${JWT_REFRESH_TTL_DAYS:-7} COOKIE_DOMAIN: ${COOKIE_DOMAIN:-ai-sandbox.oliver.solutions} COOKIE_SECURE: ${COOKIE_SECURE:-true} COOKIE_SAMESITE: ${COOKIE_SAMESITE:-Lax} # Database MONGODB_URI: mongodb://mongodb:27017/${MONGODB_DB:-accessible_video} MONGODB_DB: ${MONGODB_DB:-accessible_video} # Redis REDIS_URL: redis://redis:6379/0 CELERY_BROKER_URL: redis://redis:6379/0 CELERY_RESULT_BACKEND: redis://redis:6379/0 # GCP GCP_PROJECT_ID: ${GCP_PROJECT_ID} GCS_BUCKET: ${GCS_BUCKET:-accessible-video} GOOGLE_APPLICATION_CREDENTIALS: /secrets/gcp-credentials.json # AI Services GEMINI_API_KEY: ${GEMINI_API_KEY} TRANSLATE_API_KEY: ${TRANSLATE_API_KEY:-} ELEVENLABS_API_KEY: ${ELEVENLABS_API_KEY:-} GOOGLE_TTS_CREDENTIALS: /secrets/gcp-credentials.json # Email SENDGRID_API_KEY: ${SENDGRID_API_KEY:-} EMAIL_FROM: ${EMAIL_FROM:-noreply@ai-sandbox.oliver.solutions} CLIENT_BASE_URL: ${CLIENT_BASE_URL:-https://ai-sandbox.oliver.solutions/video-accessibility} # Microsoft Authentication AZURE_CLIENT_ID: ${AZURE_CLIENT_ID:-} AZURE_AUTHORITY: ${AZURE_AUTHORITY:-} AZURE_REDIRECT_URI: ${AZURE_REDIRECT_URI:-} # CORS CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:6001,http://localhost:5173,http://localhost:3000} # Observability SENTRY_DSN: ${SENTRY_DSN:-} # AI Cost Tracker COST_TRACKER_BASE_URL: ${COST_TRACKER_BASE_URL:-} COST_TRACKER_API_KEY: ${COST_TRACKER_API_KEY:-} COST_TRACKER_SOURCE_APP: ${COST_TRACKER_SOURCE_APP:-video-accessibility} COST_TRACKER_ENABLED: ${COST_TRACKER_ENABLED:-true} volumes: - ./secrets:/secrets:ro - whisper-worker-logs:/app/logs - shared-tmp:/shared-tmp networks: - accessible-video-network # Memory limit to prevent OOM (Whisper large-v3 model uses ~4-6GB) deploy: resources: limits: memory: 8G reservations: memory: 4G logging: driver: "json-file" options: max-size: "10m" max-file: "3" # ============================================================================= # Networks # ============================================================================= networks: accessible-video-network: driver: bridge name: accessible-video-network # ============================================================================= # Volumes # ============================================================================= volumes: mongodb-data: name: accessible-video-mongodb-data mongodb-config: name: accessible-video-mongodb-config redis-data: name: accessible-video-redis-data api-logs: name: accessible-video-api-logs worker-logs: name: accessible-video-worker-logs ffmpeg-worker-logs: name: accessible-video-ffmpeg-worker-logs tts-worker-logs: name: accessible-video-tts-worker-logs whisper-worker-logs: name: accessible-video-whisper-worker-logs shared-tmp: name: accessible-video-shared-tmp