version: "3.9" services: # PostgreSQL + TimescaleDB postgres: image: timescale/timescaledb:latest-pg16 container_name: invest-copilot-db ports: - "5432:5432" environment: POSTGRES_USER: ${POSTGRES_USER:-invest} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-invest_password} POSTGRES_DB: ${POSTGRES_DB:-invest_copilot} volumes: - postgres_data:/var/lib/postgresql/data - ./init.sql:/docker-entrypoint-initdb.d/init.sql healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-invest}"] interval: 10s timeout: 5s retries: 5 # Redis redis: image: redis:7-alpine container_name: invest-copilot-redis ports: - "6379:6379" command: redis-server --requirepass ${REDIS_PASSWORD:-redis_password} volumes: - redis_data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 # MinIO (S3-compatible object storage) minio: image: minio/minio:latest container_name: invest-copilot-minio ports: - "9000:9000" - "9001:9001" environment: MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minio_admin} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minio_password} volumes: - minio_data:/data command: server /data --console-address ":9001" healthcheck: test: ["CMD", "mc", "ready", "local"] interval: 10s timeout: 5s retries: 5 # Backend API backend: build: context: ./src/backend dockerfile: Dockerfile container_name: invest-copilot-backend ports: - "8000:8000" environment: DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-invest}:${POSTGRES_PASSWORD:-invest_password}@postgres:5432/${POSTGRES_DB:-invest_copilot} REDIS_URL: redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/0 JWT_SECRET_KEY: ${JWT_SECRET_KEY:-change-me-in-production} OPENAI_API_KEY: ${OPENAI_API_KEY:-} MINIO_ENDPOINT: minio:9000 MINIO_ACCESS_KEY: ${MINIO_ROOT_USER:-minio_admin} MINIO_SECRET_KEY: ${MINIO_ROOT_PASSWORD:-minio_password} MINIO_BUCKET: ${MINIO_BUCKET:-invest-copilot} depends_on: postgres: condition: service_healthy redis: condition: service_healthy volumes: - ./src/backend:/app command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload # Celery Worker celery-worker: build: context: ./src/backend dockerfile: Dockerfile container_name: invest-copilot-celery environment: DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-invest}:${POSTGRES_PASSWORD:-invest_password}@postgres:5432/${POSTGRES_DB:-invest_copilot} REDIS_URL: redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/0 JWT_SECRET_KEY: ${JWT_SECRET_KEY:-change-me-in-production} depends_on: postgres: condition: service_healthy redis: condition: service_healthy command: celery -A celery_app worker --loglevel=info --concurrency=4 # Celery Beat (scheduled tasks) celery-beat: build: context: ./src/backend dockerfile: Dockerfile container_name: invest-copilot-celery-beat environment: DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-invest}:${POSTGRES_PASSWORD:-invest_password}@postgres:5432/${POSTGRES_DB:-invest_copilot} REDIS_URL: redis://:${REDIS_PASSWORD:-redis_password}@redis:6379/0 depends_on: postgres: condition: service_healthy redis: condition: service_healthy command: celery -A celery_app beat --loglevel=info --scheduler celery.schedules:schedule # Frontend frontend: build: context: ./src/frontend dockerfile: Dockerfile container_name: invest-copilot-frontend ports: - "3000:3000" environment: NEXT_PUBLIC_API_URL: http://localhost:8000/api/v1 depends_on: - backend volumes: postgres_data: redis_data: minio_data: networks: invest-net: driver: bridge # Attach services (add to each service: networks: [invest-net] if needed for explicit bridge)