services: # Nginx reverse proxy - Internal routing only (since SSL is handled by host) enclava-nginx: image: nginx:alpine ports: - "50080:80" # Port for host reverse proxy to connect to volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - nginx-logs:/var/log/nginx depends_on: - enclava-backend - enclava-frontend networks: - enclava-net restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost/health"] interval: 30s timeout: 10s retries: 3 # Database migration service - runs once to apply migrations enclava-migrate: build: context: ./backend dockerfile: Dockerfile.prod env_file: - ./.env environment: - DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@enclava-postgres:5432/${POSTGRES_DB} depends_on: - enclava-postgres command: ["/usr/local/bin/migrate.sh"] networks: - enclava-net restart: "no" # Run once and exit # Main application backend - Production version enclava-backend: build: context: ./backend dockerfile: Dockerfile.prod env_file: - ./.env environment: - DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@enclava-postgres:5432/${POSTGRES_DB} - REDIS_URL=redis://enclava-redis:6379 - QDRANT_HOST=enclava-qdrant - JWT_SECRET=${JWT_SECRET} - PRIVATEMODE_API_KEY=${PRIVATEMODE_API_KEY} - ADMIN_EMAIL=${ADMIN_EMAIL} - ADMIN_PASSWORD=${ADMIN_PASSWORD} - LOG_LLM_PROMPTS=${LOG_LLM_PROMPTS:-false} - BASE_URL=${BASE_URL} - NODE_ENV=production - APP_ENV=production depends_on: - enclava-migrate - enclava-postgres - enclava-redis - enclava-qdrant - privatemode-proxy volumes: - ./logs:/app/logs - ./plugins:/plugins networks: - enclava-net restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 # Next.js frontend - Production build enclava-frontend: build: context: ./frontend dockerfile: Dockerfile target: runner # Use the production stage from multi-stage build env_file: - ./.env environment: - BASE_URL=${BASE_URL} - NEXT_PUBLIC_BASE_URL=${BASE_URL} - INTERNAL_API_URL=http://enclava-backend:8000 - NODE_ENV=production - NEXT_TELEMETRY_DISABLED=1 depends_on: - enclava-backend networks: - enclava-net restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000"] interval: 30s timeout: 10s retries: 3 # PostgreSQL database enclava-postgres: image: postgres:16-alpine environment: - POSTGRES_DB=${POSTGRES_DB} - POSTGRES_USER=${POSTGRES_USER} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} volumes: - enclava-postgres-data:/var/lib/postgresql/data - ./postgres/postgresql.conf:/etc/postgresql/postgresql.conf:ro networks: - enclava-net restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] interval: 30s timeout: 10s retries: 5 # Redis for caching and message queue enclava-redis: image: redis:7-alpine command: redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru volumes: - enclava-redis-data:/data networks: - enclava-net restart: unless-stopped healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 30s timeout: 10s retries: 3 # Qdrant vector database enclava-qdrant: image: qdrant/qdrant:v1.7.4 environment: - QDRANT__SERVICE__HTTP_PORT=6333 - QDRANT__SERVICE__GRPC_PORT=6334 - QDRANT__LOG_LEVEL=INFO volumes: - enclava-qdrant-data:/qdrant/storage networks: - enclava-net restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:6333/"] interval: 30s timeout: 10s retries: 3 # Privatemode.ai service (optional - for confidential models) privatemode-proxy: image: ghcr.io/edgelesssys/privatemode/privatemode-proxy:latest environment: - PRIVATEMODE_API_KEY=${PRIVATEMODE_API_KEY} - PRIVATEMODE_CACHE_MODE=${PRIVATEMODE_CACHE_MODE:-none} - PRIVATEMODE_CACHE_SALT=${PRIVATEMODE_CACHE_SALT:-} entrypoint: ["/bin/privatemode-proxy"] command: [ "--apiKey=${PRIVATEMODE_API_KEY}", "--port=8080" ] networks: - enclava-net restart: unless-stopped volumes: enclava-postgres-data: driver: local enclava-redis-data: driver: local enclava-qdrant-data: driver: local nginx-logs: driver: local networks: enclava-net: driver: bridge