# ./docker-compose.yml # Defines Docker services, networks, and volumes for easy deployment services: mysql: image: mysql:8.0 container_name: transcriber-mysql environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: ${MYSQL_DB} MYSQL_USER: ${MYSQL_USER} MYSQL_PASSWORD: ${MYSQL_PASSWORD} volumes: - mysql_data:/var/lib/mysql ports: - "${MYSQL_HOST_PORT:-3307}:3306" # Expose MySQL to host on MYSQL_HOST_PORT (default 3307) restart: unless-stopped healthcheck: test: ["CMD", "mysql", "-h", "localhost", "-u", "${MYSQL_USER}", "-p${MYSQL_PASSWORD}", "-e", "SELECT 1"] interval: 10s timeout: 5s retries: 5 transcriber-platform: build: context: . dockerfile: Dockerfile container_name: transcriber-platform ports: - "${APP_PORT:-5004}:5004" # Expose app on APP_PORT (default 5004) volumes: - ./app:/app/app - ./tests:/app/tests - ./logs:/app/logs - ./uploads:/app/uploads - ./runtime:/app/runtime env_file: - .env # Load variables from .env file environment: # --- Core App Settings --- SECRET_KEY: ${SECRET_KEY} # CRITICAL: Must be set in .env DEPLOYMENT_MODE: ${DEPLOYMENT_MODE:-multi} START_EMBEDDED_WORKER: "0" # Compose runs the separate transcriber-worker service TZ: ${TZ:-UTC} # --- API Keys (Global Fallbacks / Single-User Mode) --- ASSEMBLYAI_API_KEY: ${ASSEMBLYAI_API_KEY} OPENAI_API_KEY: ${OPENAI_API_KEY} GEMINI_API_KEY: ${GEMINI_API_KEY} ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} OPENROUTER_API_KEY: ${OPENROUTER_API_KEY} # --- Provider Configuration --- TRANSCRIPTION_PROVIDERS: ${TRANSCRIPTION_PROVIDERS:-assemblyai,openai,gemini,openrouter} DEFAULT_TRANSCRIPTION_PROVIDER: ${DEFAULT_TRANSCRIPTION_PROVIDER:-gpt-4o-transcribe} DEFAULT_LLM_PROVIDER: ${DEFAULT_LLM_PROVIDER:-gemini} # --- Transcription Language Settings --- DEFAULT_LANGUAGE: ${DEFAULT_LANGUAGE:-auto} SUPPORTED_LANGUAGE_CODES: ${SUPPORTED_LANGUAGE_CODES:-en,nl,fr,es} # --- Database (MySQL Configuration) --- MYSQL_HOST: mysql # Service name defined above MYSQL_PORT: 3306 # Internal port for MySQL service MYSQL_USER: ${MYSQL_USER} MYSQL_PASSWORD: ${MYSQL_PASSWORD} MYSQL_DB: ${MYSQL_DB} MYSQL_POOL_SIZE: ${MYSQL_POOL_SIZE:-10} # --- File Storage & Cleanup --- DELETE_THRESHOLD: ${DELETE_THRESHOLD:-86400} # 24 hours in seconds # --- Logging --- LOG_LEVEL: ${LOG_LEVEL:-INFO} # --- Runtime State / Markers --- # TASK_LOCK_FILE is defined in config.py using RUNTIME_DIR, no need to set here if using defaults # --- Security Settings --- BCRYPT_LOG_ROUNDS: ${BCRYPT_LOG_ROUNDS:-12} RATELIMIT_DEFAULT: ${RATELIMIT_DEFAULT:-600 per minute;10000 per day} # --- Admin User Credentials (for initial setup) --- ADMIN_USERNAME: ${ADMIN_USERNAME:-admin} ADMIN_PASSWORD: ${ADMIN_PASSWORD:-password} # WARNING: default for first-time Docker install; override in production # CRITICAL: Must be set in .env ADMIN_EMAIL: ${ADMIN_EMAIL} # --- Email Configuration --- MAIL_SERVER: ${MAIL_SERVER} MAIL_PORT: ${MAIL_PORT:-587} MAIL_USE_TLS: ${MAIL_USE_TLS:-true} MAIL_USE_SSL: ${MAIL_USE_SSL:-false} MAIL_USERNAME: ${MAIL_USERNAME} MAIL_PASSWORD: ${MAIL_PASSWORD} MAIL_DEFAULT_SENDER: ${MAIL_DEFAULT_SENDER} MAIL_DEBUG: ${MAIL_DEBUG:-false} # --- OAuth Configuration --- GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID} # --- Data Retention Configuration --- PHYSICAL_DELETION_DAYS: ${PHYSICAL_DELETION_DAYS:-120} # --- Workflow and Auxiliary LLM Defaults --- WORKFLOW_LLM_PROVIDER: ${WORKFLOW_LLM_PROVIDER:-OPENROUTER} WORKFLOW_LLM_MODEL: ${WORKFLOW_LLM_MODEL:-google/gemini-3.7-flash} TITLE_GENERATION_LLM_PROVIDER: ${TITLE_GENERATION_LLM_PROVIDER:-GEMINI} TITLE_GENERATION_LLM_MODEL: ${TITLE_GENERATION_LLM_MODEL:-gemma-4-26b-a4b-it} WORKFLOW_MAX_OUTPUT_TOKENS: ${WORKFLOW_MAX_OUTPUT_TOKENS:-1024} WORKFLOW_RATE_LIMIT: ${WORKFLOW_RATE_LIMIT:-10 per hour} DIRECT_LLM_RATE_LIMIT: ${DIRECT_LLM_RATE_LIMIT:-5 per hour} # --- Transcription Performance --- TRANSCRIPTION_MAX_CONCURRENT_JOBS: ${TRANSCRIPTION_MAX_CONCURRENT_JOBS:-2} TRANSCRIPTION_SLOT_POLL_SECONDS: ${TRANSCRIPTION_SLOT_POLL_SECONDS:-2} TRANSCRIPTION_ABANDONED_JOB_SECONDS: ${TRANSCRIPTION_ABANDONED_JOB_SECONDS:-300} TRANSCRIPTION_WORKERS: ${TRANSCRIPTION_WORKERS:-4} depends_on: mysql: condition: service_healthy healthcheck: test: ["CMD-SHELL", "curl -fsS http://localhost:5004/ >/dev/null || exit 1"] interval: 10s timeout: 5s retries: 12 restart: unless-stopped # Durable transcription, workflow, and title jobs run outside Gunicorn so # web-worker restarts do not lose accepted work. transcriber-worker: build: context: . dockerfile: Dockerfile container_name: transcriber-platform-worker command: ["/app/.local/bin/flask", "worker"] env_file: - .env environment: FLASK_APP: app MYSQL_HOST: mysql MYSQL_PORT: 3306 volumes: - ./app:/app/app - ./logs:/app/logs - ./uploads:/app/uploads - ./runtime:/app/runtime depends_on: mysql: condition: service_healthy transcriber-platform: condition: service_healthy restart: unless-stopped # Retention runs as one visible process instead of being tied to a Gunicorn # worker. It shares the upload, log, and runtime volumes with the web app. transcriber-cleanup: build: context: . dockerfile: Dockerfile container_name: transcriber-platform-cleanup command: ["/app/.local/bin/flask", "cleanup-loop"] env_file: - .env environment: FLASK_APP: app MYSQL_HOST: mysql MYSQL_PORT: 3306 volumes: - ./logs:/app/logs - ./uploads:/app/uploads - ./runtime:/app/runtime depends_on: mysql: condition: service_healthy transcriber-platform: condition: service_healthy restart: unless-stopped volumes: mysql_data: # Persist MySQL data