services: db: image: postgres:16-alpine environment: POSTGRES_USER: ${POSTGRES_USER:-langlearn} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB:-langlearn} volumes: - pgdata:/var/lib/postgresql/data ports: - "5432:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-langlearn}"] interval: 5s timeout: 5s retries: 10 storage: image: minio/minio:latest command: server /data --console-address ":9001" environment: MINIO_ROOT_USER: ${STORAGE_ACCESS_KEY:-langlearn} MINIO_ROOT_PASSWORD: ${STORAGE_SECRET_KEY} ports: - "9000:9000" - "9001:9001" volumes: - storagedata:/data healthcheck: test: [ "CMD-SHELL", "curl -sf http://localhost:9000/minio/health/live || exit 1", ] interval: 5s timeout: 5s retries: 10 api: build: ./api volumes: - ./api:/app:z ports: - "${API_PORT:-8000}:8000" command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload environment: DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-langlearn}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-langlearn} PROCRASTINATEDATABASE_URL: postgresql://${POSTGRES_USER:-langlearn}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-langlearn} ADMIN_USER_EMAILS: ${ADMIN_USER_EMAILS:-wilson@thomaswilson.xyz} API_BASE_URL: ${API_BASE_URL:-http://localhost:8000} JWT_SECRET: ${JWT_SECRET} ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} DEEPL_API_KEY: ${DEEPL_API_KEY} DEEPGRAM_API_KEY: ${DEEPGRAM_API_KEY} GEMINI_API_KEY: ${GEMINI_API_KEY} PYTHONPATH: /app STORAGE_ENDPOINT_URL: http://storage:9000 STORAGE_ACCESS_KEY: ${STORAGE_ACCESS_KEY:-langlearn} STORAGE_SECRET_KEY: ${STORAGE_SECRET_KEY} STORAGE_BUCKET: ${STORAGE_BUCKET:-langlearn} TRANSACTIONAL_EMAIL_PROVIDER: ${TRANSACTIONAL_EMAIL_PROVIDER:-stub} OTEL_SERVICE_NAME: ${OTEL_SERVICE_NAME:-language-learning-api} OTEL_EXPORTER_PROMETHEUS_HOST: 0.0.0.0 OTEL_EXPORTER_PROMETHEUS_PORT: ${OTEL_EXPORTER_PROMETHEUS_PORT:-9464} depends_on: db: condition: service_healthy storage: condition: service_healthy restart: unless-stopped worker: build: ./api volumes: - ./api:/app:z command: watchfiles --filter python "python -m worker.main" /app environment: DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-langlearn}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-langlearn} PROCRASTINATEDATABASE_URL: postgresql://${POSTGRES_USER:-langlearn}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-langlearn} JWT_SECRET: ${JWT_SECRET} ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} DEEPL_API_KEY: ${DEEPL_API_KEY} DEEPGRAM_API_KEY: ${DEEPGRAM_API_KEY} GEMINI_API_KEY: ${GEMINI_API_KEY} PYTHONPATH: /app STORAGE_ENDPOINT_URL: http://storage:9000 STORAGE_ACCESS_KEY: ${STORAGE_ACCESS_KEY:-langlearn} STORAGE_SECRET_KEY: ${STORAGE_SECRET_KEY} STORAGE_BUCKET: ${STORAGE_BUCKET:-langlearn} OTEL_SERVICE_NAME: ${OTEL_SERVICE_NAME:-language-learning-worker} depends_on: db: condition: service_healthy storage: condition: service_healthy restart: unless-stopped frontend: build: context: ./frontend args: PUBLIC_API_BASE_URL: ${PUBLIC_API_BASE_URL:-http://localhost:8000} ports: - "${FRONTEND_PORT:-3001}:3000" environment: ORIGIN: ${ORIGIN:-http://localhost:3001} depends_on: - api restart: unless-stopped prometheus: image: prom/prometheus:v2.54.1 command: - --config.file=/etc/prometheus/prometheus.yml - --storage.tsdb.path=/prometheus volumes: - ./monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro - prometheusdata:/prometheus ports: - "9090:9090" depends_on: - api restart: unless-stopped loki: image: grafana/loki:3.1.1 command: -config.file=/etc/loki/loki-config.yml volumes: - ./monitoring/loki/loki-config.yml:/etc/loki/loki-config.yml:ro - lokidata:/loki ports: - "3100:3100" restart: unless-stopped alloy: image: grafana/alloy:v1.7.1 user: "0:0" security_opt: - label=disable command: - run - --server.http.listen-addr=0.0.0.0:12345 - --storage.path=/var/lib/alloy/data - /etc/alloy/config.alloy volumes: - ./monitoring/alloy/config.alloy:/etc/alloy/config.alloy:ro - /var/run/docker.sock:/var/run/docker.sock:ro - alloydata:/var/lib/alloy/data ports: - "12345:12345" depends_on: - loki restart: unless-stopped grafana: image: grafana/grafana:11.2.0 volumes: - ./monitoring/grafana/provisioning:/etc/grafana/provisioning:ro - grafanadata:/var/lib/grafana ports: - "3000:3000" environment: GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER:-admin} GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:-admin} GF_USERS_ALLOW_SIGN_UP: "false" GF_AUTH_ANONYMOUS_ENABLED: "false" depends_on: - prometheus - loki restart: unless-stopped volumes: pgdata: storagedata: prometheusdata: grafanadata: lokidata: alloydata: