services: # ============ Database ============ postgres: image: timescale/timescaledb:latest-pg16 container_name: volatio_postgres environment: POSTGRES_USER: volatio POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-vibes123} POSTGRES_DB: volatio_market volumes: - postgres_data:/var/lib/postgresql/data restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U volatio"] interval: 10s timeout: 5s retries: 5 redis: image: redis:7-alpine container_name: volatio_redis volumes: - redis_data:/data restart: unless-stopped healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 # ============ Applications ============ # Database migrations - runs once before API starts # IMPORTANT: Uses same image as api to avoid cache inconsistency migrate: image: volatio-api container_name: volatio_migrate dns_search: [] env_file: - .env depends_on: postgres: condition: service_healthy # Run migration and exit command: ["bun", "packages/db/src/migrate.ts"] restart: "no" api: build: context: . dockerfile: ./apps/api/Dockerfile container_name: volatio_api dns_search: [] environment: - NODE_ENV=production - LOG_LEVEL=info - BROWSER_PROFILES_DIR=/data/browser-profiles env_file: - .env volumes: - browser_profiles:/data/browser-profiles depends_on: postgres: condition: service_healthy redis: condition: service_healthy migrate: condition: service_completed_successfully restart: unless-stopped # Log rotation: 50MB per file, keep 5 files (250MB total) logging: driver: "json-file" options: max-size: "50m" max-file: "5" healthcheck: test: ["CMD-SHELL", "curl -sf http://localhost:3001/health || exit 1"] interval: 30s timeout: 10s retries: 3 start_period: 30s dashboard: build: context: . dockerfile: ./apps/dashboard/Dockerfile container_name: volatio_dashboard dns_search: [] environment: - NODE_ENV=production env_file: - .env depends_on: - api restart: unless-stopped # Log rotation: 50MB per file, keep 5 files (250MB total) logging: driver: "json-file" options: max-size: "50m" max-file: "5" healthcheck: test: ["CMD-SHELL", "wget -q --spider http://localhost:3000/api/health || exit 1"] interval: 30s timeout: 10s retries: 3 start_period: 60s ml: build: ./services/ml container_name: volatio_ml environment: - PYTHONUNBUFFERED=1 env_file: - .env restart: unless-stopped jobs: build: context: . dockerfile: packages/jobs/Dockerfile container_name: volatio_jobs environment: - NODE_ENV=production - LOG_LEVEL=info - DATABASE_URL=postgres://volatio:${POSTGRES_PASSWORD:-vibes123}@postgres:5432/volatio_market - REDIS_URL=redis://redis:6379 - S3_BUCKET=volatio-data - AWS_REGION=us-east-1 - BROWSER_PROFILES_DIR=/data/browser-profiles env_file: - .env volumes: - browser_profiles:/data/browser-profiles depends_on: postgres: condition: service_healthy redis: condition: service_healthy restart: unless-stopped # Log rotation: 50MB per file, keep 5 files (250MB total) logging: driver: "json-file" options: max-size: "50m" max-file: "5" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3099/health"] interval: 30s timeout: 10s retries: 3 start_period: 10s # ============ Reverse Proxy ============ caddy: image: caddy:2-alpine container_name: volatio_caddy dns_search: [] ports: - "80:80" - "443:443" volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config depends_on: - api - dashboard restart: unless-stopped # ============ Maintenance ============ # Runs backups automatically. No host cron required. backup: image: postgres:16-alpine container_name: volatio_backup environment: POSTGRES_HOST: postgres POSTGRES_USER: volatio POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-vibes123} AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} S3_BUCKET: ${S3_BUCKET} volumes: - ./scripts/backup-databases.sh:/usr/local/bin/backup-databases.sh:ro entrypoint: | /bin/sh -c " apk add --no-cache aws-cli && echo '0 3 * * * /usr/local/bin/backup-databases.sh' > /etc/crontabs/root && crond -f -l 8 " depends_on: - postgres restart: unless-stopped volumes: postgres_data: redis_data: caddy_data: caddy_config: browser_profiles: