version: '3.8' services: # ================================================================ # MariaDB Database (Shared) # ================================================================ mariadb: image: mariadb:latest container_name: evoting_db restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-rootpass123} MYSQL_DATABASE: ${DB_NAME:-evoting_db} MYSQL_USER: ${DB_USER:-evoting_user} MYSQL_PASSWORD: ${DB_PASSWORD:-evoting_pass123} MYSQL_INITDB_SKIP_TZINFO: 1 ports: - "${DB_PORT:-3306}:3306" volumes: - evoting_data:/var/lib/mysql - ./docker/init.sql:/docker-entrypoint-initdb.d/01-init.sql - ./docker/populate_past_elections.sql:/docker-entrypoint-initdb.d/02-populate.sql - ./docker/create_active_election.sql:/docker-entrypoint-initdb.d/03-active.sql networks: - evoting_network healthcheck: test: ["CMD", "mariadb-admin", "ping", "-h", "localhost", "--silent"] timeout: 20s retries: 10 start_period: 40s # ================================================================ # Backend Node 1 (Internal Port 8000, No external binding) # ================================================================ backend-node-1: build: context: . dockerfile: docker/Dockerfile.backend container_name: evoting_backend_node1 restart: unless-stopped environment: DB_HOST: mariadb DB_PORT: 3306 DB_NAME: ${DB_NAME:-evoting_db} DB_USER: ${DB_USER:-evoting_user} DB_PASSWORD: ${DB_PASSWORD:-evoting_pass123} SECRET_KEY: ${SECRET_KEY:-your-secret-key-change-in-production} DEBUG: ${DEBUG:-false} PYTHONUNBUFFERED: 1 NODE_ID: node1 NODE_PORT: 8000 expose: - "8000" depends_on: mariadb: condition: service_healthy volumes: - ./backend:/app/backend - backend_cache_1:/app/.cache networks: - evoting_network command: uvicorn backend.main:app --host 0.0.0.0 --port 8000 --reload healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s # ================================================================ # Backend Node 2 (Internal Port 8000, No external binding) # ================================================================ backend-node-2: build: context: . dockerfile: docker/Dockerfile.backend container_name: evoting_backend_node2 restart: unless-stopped environment: DB_HOST: mariadb DB_PORT: 3306 DB_NAME: ${DB_NAME:-evoting_db} DB_USER: ${DB_USER:-evoting_user} DB_PASSWORD: ${DB_PASSWORD:-evoting_pass123} SECRET_KEY: ${SECRET_KEY:-your-secret-key-change-in-production} DEBUG: ${DEBUG:-false} PYTHONUNBUFFERED: 1 NODE_ID: node2 NODE_PORT: 8000 expose: - "8000" depends_on: mariadb: condition: service_healthy volumes: - ./backend:/app/backend - backend_cache_2:/app/.cache networks: - evoting_network command: uvicorn backend.main:app --host 0.0.0.0 --port 8000 --reload healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s # ================================================================ # Backend Node 3 (Internal Port 8000, No external binding) # ================================================================ backend-node-3: build: context: . dockerfile: docker/Dockerfile.backend container_name: evoting_backend_node3 restart: unless-stopped environment: DB_HOST: mariadb DB_PORT: 3306 DB_NAME: ${DB_NAME:-evoting_db} DB_USER: ${DB_USER:-evoting_user} DB_PASSWORD: ${DB_PASSWORD:-evoting_pass123} SECRET_KEY: ${SECRET_KEY:-your-secret-key-change-in-production} DEBUG: ${DEBUG:-false} PYTHONUNBUFFERED: 1 NODE_ID: node3 NODE_PORT: 8000 expose: - "8000" depends_on: mariadb: condition: service_healthy volumes: - ./backend:/app/backend - backend_cache_3:/app/.cache networks: - evoting_network command: uvicorn backend.main:app --host 0.0.0.0 --port 8000 --reload healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s # ================================================================ # Nginx Load Balancer (Reverse Proxy) # Routes to all backend nodes on port 8000 # ================================================================ nginx: image: nginx:latest container_name: evoting_nginx restart: unless-stopped ports: - "8000:8000" volumes: - ./docker/nginx.conf:/etc/nginx/nginx.conf:ro depends_on: - backend-node-1 - backend-node-2 - backend-node-3 networks: - evoting_network healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 # ================================================================ # Frontend Next.js Service # ================================================================ frontend: build: context: . dockerfile: docker/Dockerfile.frontend args: NEXT_PUBLIC_API_URL: http://localhost:8000 container_name: evoting_frontend restart: unless-stopped ports: - "${FRONTEND_PORT:-3000}:3000" depends_on: - nginx environment: NEXT_PUBLIC_API_URL: http://localhost:8000 NODE_ENV: production networks: - evoting_network healthcheck: test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/"] interval: 30s timeout: 10s retries: 3 start_period: 40s # ================================================================ # Adminer (Database Management UI) # ================================================================ adminer: image: adminer:latest container_name: evoting_adminer restart: unless-stopped ports: - "8081:8080" depends_on: - mariadb networks: - evoting_network environment: ADMINER_DEFAULT_SERVER: mariadb volumes: evoting_data: driver: local backend_cache_1: driver: local backend_cache_2: driver: local backend_cache_3: driver: local networks: evoting_network: driver: bridge ipam: config: - subnet: 172.25.0.0/16