services: # PostgreSQL Database postgres: image: postgres:15-alpine container_name: haproxy-openmanager-db environment: POSTGRES_DB: haproxy_openmanager POSTGRES_USER: haproxy_user POSTGRES_PASSWORD: haproxy_pass PGDATA: /var/lib/postgresql/data/pgdata volumes: - postgres_data:/var/lib/postgresql/data ports: - "5432:5432" networks: - haproxy-network healthcheck: test: ["CMD-SHELL", "pg_isready -U haproxy_user -d haproxy_openmanager"] interval: 10s timeout: 5s retries: 5 # Redis Cache redis: image: redis:8.8.0-alpine container_name: haproxy-openmanager-redis command: redis-server --maxmemory 2gb --maxmemory-policy volatile-lru --save "" ports: - "6379:6379" networks: - haproxy-network healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 3s retries: 5 # Backend API - pulls from Docker Hub by default # To build locally instead, uncomment the build section and run: docker compose build backend backend: image: taylanbakircioglu/haproxy-openmanager-backend:latest # build: # context: ./backend # dockerfile: Dockerfile container_name: haproxy-openmanager-backend environment: - DATABASE_URL=postgresql://haproxy_user:haproxy_pass@postgres:5432/haproxy_openmanager - REDIS_URL=redis://redis:6379 - SECRET_KEY=your-secret-key-change-this-in-production - DEBUG=False - LOG_LEVEL=INFO # Interpolated, not hardcoded: these were literals, so a value set in the # host environment or .env was silently ignored and every install kept the # localhost default. That default is also what the ACME challenge backend # falls back to, and HAProxy resolves it ON THE HAPROXY NODE — so on any # deployment where HAProxy is not this machine, it points at the wrong box. - PUBLIC_URL=${PUBLIC_URL:-http://localhost:8080} - MANAGEMENT_BASE_URL=${MANAGEMENT_BASE_URL:-http://localhost:8080} # Empty when unset on the host: the image CMD then falls back to # WEB_CONCURRENCY (uvicorn's native env) and finally to 1. - UVICORN_WORKERS=${UVICORN_WORKERS:-} volumes: - haproxy_configs:/etc/haproxy # NOT published to the host: the API is reachable only through the nginx # service (host :8080). Host port 8000 is therefore NOT this API — on a box # running Portainer it is Portainer's edge tunnel, which answers 404 and looks # deceptively like a working challenge endpoint. expose: - "8000" depends_on: postgres: condition: service_healthy redis: condition: service_healthy networks: - haproxy-network healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health')"] interval: 30s timeout: 10s retries: 3 start_period: 40s # Frontend React App - pulls from Docker Hub by default # To build locally instead, uncomment the build section and run: docker compose build frontend # # NOTE: REACT_APP_* env vars are BUILD-time only for Create-React-App. The # runtime container (serve -s build) does NOT consume them. The frontend # uses same-origin (window.location) for /api/* and is routed by the nginx # service below to the backend container. No env vars are required here. frontend: image: taylanbakircioglu/haproxy-openmanager-frontend:latest # build: # context: ./frontend # dockerfile: Dockerfile container_name: haproxy-openmanager-frontend expose: - "3000" depends_on: - backend networks: - haproxy-network healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000"] interval: 30s timeout: 10s retries: 3 # Nginx Reverse Proxy nginx: # Pinned to a patched release for the nginx "poolslip" advisory # (mainline <=1.31.0 affected; fixed in mainline 1.31.1+ / stable 1.30.2+). image: nginx:1.31.1-alpine container_name: haproxy-openmanager-nginx volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro ports: - "8080:8080" depends_on: - frontend - backend networks: - haproxy-network healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"] interval: 30s timeout: 10s retries: 3 # HAProxy Instance (for testing) haproxy: image: haproxy:2.8-alpine container_name: haproxy-instance volumes: - ./haproxy/haproxy-simple.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro ports: - "80:80" - "8404:8404" networks: - haproxy-network # Remote HAProxy Instance 1 (Production) haproxy-remote1: image: haproxy:2.8-alpine container_name: haproxy-remote1 volumes: - ./haproxy/haproxy-simple.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro ports: - "8001:80" - "8405:8404" networks: - haproxy-network environment: - HAPROXY_ENV=production # Remote HAProxy Instance 2 (Staging) haproxy-remote2: image: haproxy:2.8-alpine container_name: haproxy-remote2 volumes: - ./haproxy/haproxy-simple.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro ports: - "8002:80" - "8406:8404" networks: - haproxy-network environment: - HAPROXY_ENV=staging volumes: postgres_data: haproxy_configs: networks: haproxy-network: driver: bridge