services: # ── Database ────────────────────────────────────────────── postgres: image: pgvector/pgvector:pg16 restart: unless-stopped environment: POSTGRES_DB: nexusai POSTGRES_USER: nexusai POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-nexusai_secret} volumes: - postgres_data:/var/lib/postgresql/data - ./infra/docker/init.sql:/docker-entrypoint-initdb.d/init.sql ports: - "5432:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U nexusai"] interval: 10s timeout: 5s retries: 5 redis: image: redis:7-alpine restart: unless-stopped command: redis-server --requirepass ${REDIS_PASSWORD:-redis_secret} volumes: - redis_data:/data ports: - "6379:6379" healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 # ── Backend ─────────────────────────────────────────────── backend: build: context: ./backend dockerfile: Dockerfile restart: unless-stopped env_file: .env environment: DATABASE_URL: postgresql+asyncpg://nexusai:${POSTGRES_PASSWORD:-nexusai_secret}@postgres:5432/nexusai REDIS_URL: redis://:${REDIS_PASSWORD:-redis_secret}@redis:6379/0 ports: - "8000:8000" volumes: - ./backend:/app - uploads_data:/app/uploads depends_on: postgres: condition: service_healthy redis: condition: service_healthy healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 # ── Worker (Celery) ─────────────────────────────────────── worker: build: context: ./backend dockerfile: Dockerfile restart: unless-stopped command: celery -A app.worker worker --loglevel=info --concurrency=4 env_file: .env environment: DATABASE_URL: postgresql+asyncpg://nexusai:${POSTGRES_PASSWORD:-nexusai_secret}@postgres:5432/nexusai REDIS_URL: redis://:${REDIS_PASSWORD:-redis_secret}@redis:6379/0 volumes: - ./backend:/app - uploads_data:/app/uploads depends_on: - backend - redis # ── Frontend ────────────────────────────────────────────── frontend: build: context: ./frontend dockerfile: Dockerfile restart: unless-stopped env_file: .env ports: - "3000:3000" depends_on: - backend # ── Nginx reverse proxy ─────────────────────────────────── nginx: image: nginx:alpine restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./infra/nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./infra/nginx/certs:/etc/nginx/certs:ro depends_on: - backend - frontend # ── Monitoring ──────────────────────────────────────────── prometheus: image: prom/prometheus:latest restart: unless-stopped volumes: - ./infra/monitoring/prometheus.yml:/etc/prometheus/prometheus.yml - prometheus_data:/prometheus ports: - "9090:9090" grafana: image: grafana/grafana:latest restart: unless-stopped environment: GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD:-admin} volumes: - grafana_data:/var/lib/grafana - ./infra/monitoring/grafana:/etc/grafana/provisioning ports: - "3001:3000" depends_on: - prometheus volumes: postgres_data: redis_data: uploads_data: prometheus_data: grafana_data: