From 0e58b7bea128e769ade96720b13307306f5c8d66 Mon Sep 17 00:00:00 2001 From: Contributor Date: Sat, 3 Jan 2026 18:01:30 +0000 Subject: [PATCH] Fix #393: Add explicit network configuration for Docker Swarm deployments Services can only discover each other by hostname within the same Docker network. This fix adds an explicit `patchmon-internal` bridge network that all services (database, redis, backend, frontend) connect to, enabling reliable service discovery in both Docker Compose and Docker Swarm environments. Changes: - Added `patchmon-internal` network definition to docker-compose.yml and docker-compose.dev.yml - Connected all services to the internal network for service-to-service communication - Added comprehensive Docker Swarm documentation to docker/README.md with network configuration guidance and troubleshooting for the "host not found in upstream" error This resolves the nginx error users experienced when deploying to Docker Swarm with the frontend on a separate network from the backend. --- docker/README.md | 73 +++++++++++++++++++++++++++++++++++ docker/docker-compose.dev.yml | 12 ++++++ docker/docker-compose.yml | 12 ++++++ 3 files changed, 97 insertions(+) diff --git a/docker/README.md b/docker/README.md index 32409c4..7ee6fdb 100644 --- a/docker/README.md +++ b/docker/README.md @@ -225,6 +225,79 @@ If you wish to bind either if their respective container paths to a host path ra --- +## Docker Swarm Deployment + +> [!NOTE] +> This section covers deploying PatchMon to a Docker Swarm cluster. For standard Docker Compose deployments on a single host, use the production deployment guide above. + +### Network Configuration + +When deploying to Docker Swarm with a reverse proxy (e.g., Traefik), proper network configuration is critical. The default `docker-compose.yml` uses an internal bridge network that enables service-to-service communication: + +```yaml +networks: + patchmon-internal: + driver: bridge +``` + +All services (database, redis, backend, and frontend) connect to this internal network, allowing them to discover each other by service name. + +**Important**: If you're using an external reverse proxy network (like `traefik-net`), ensure that: + +1. All PatchMon services remain on the `patchmon-internal` network for internal communication +2. The frontend service (NGINX) can be configured to also bind to the reverse proxy network if needed +3. Service names resolve correctly within the same network + +### Service Discovery in Swarm + +In Docker Swarm, service discovery works through: +- **Service Name Resolution**: Service names resolve to virtual IPs within the same network +- **Load Balancing**: Requests to a service name are automatically load-balanced across all replicas +- **Network Isolation**: Services on different networks cannot communicate directly + +### Configuration for Swarm with Traefik + +If you're using Traefik as a reverse proxy: + +1. Keep the default `patchmon-internal` network for backend services +2. Configure Traefik in your Swarm deployment with its own network +3. Ensure the frontend service can reach the backend through the internal network + +Example modification for Swarm: + +```yaml +services: + frontend: + image: ghcr.io/patchmon/patchmon-frontend:latest + networks: + - patchmon-internal + deploy: + replicas: 1 + labels: + - "traefik.enable=true" + - "traefik.http.routers.patchmon.rule=Host(`patchmon.my.domain`)" + # ... other Traefik labels +``` + +The frontend reaches the backend via the `patchmon-internal` network using the hostname `backend`, while Traefik routes external traffic to the frontend service. + +### Troubleshooting Network Issues + +**Error: `host not found in upstream "backend"`** + +This typically occurs when: +1. Frontend and backend services are on different networks +2. Services haven't fully started (check health checks) +3. Service names haven't propagated through DNS + +**Solution**: +- Verify all services are on the same internal network +- Check service health status: `docker ps` (production) or `docker service ps` (Swarm) +- Wait for health checks to pass before accessing the application +- Confirm network connectivity: `docker exec ping backend` + +--- + # Development This section is for developers who want to contribute to PatchMon or run it in development mode. diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 351f6fc..c82c671 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -12,6 +12,8 @@ services: - "5432:5432" volumes: - ./compose_dev_data/db:/var/lib/postgresql/data + networks: + - patchmon-internal healthcheck: test: ["CMD-SHELL", "pg_isready -U patchmon_user -d patchmon_db"] interval: 3s @@ -28,6 +30,8 @@ services: - "6379:6379" volumes: - ./compose_dev_data/redis:/data + networks: + - patchmon-internal healthcheck: test: ["CMD", "redis-cli", "--no-auth-warning", "-a", "1NS3CU6E_DEV_R3DIS_PASSW0RD", "ping"] interval: 3s @@ -75,6 +79,8 @@ services: volumes: - ./compose_dev_data/agents:/app/agents - ./compose_dev_data/assets:/app/assets + networks: + - patchmon-internal depends_on: database: condition: service_healthy @@ -107,6 +113,8 @@ services: - "3000:3000" volumes: - ./compose_dev_data/assets:/app/frontend/public/assets + networks: + - patchmon-internal depends_on: backend: condition: service_healthy @@ -119,3 +127,7 @@ services: - node_modules/ - action: rebuild path: ../frontend/package.json + +networks: + patchmon-internal: + driver: bridge diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 746d933..8d23d51 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -26,6 +26,8 @@ services: POSTGRES_PASSWORD: # CREATE A STRONG DB PASSWORD AND PUT IT HERE volumes: - postgres_data:/var/lib/postgresql/data + networks: + - patchmon-internal healthcheck: test: ["CMD-SHELL", "pg_isready -U patchmon_user -d patchmon_db"] interval: 3s @@ -38,6 +40,8 @@ services: command: redis-server --requirepass your-redis-password-here # CHANGE THIS TO YOUR REDIS PASSWORD volumes: - redis_data:/data + networks: + - patchmon-internal healthcheck: test: ["CMD", "redis-cli", "--no-auth-warning", "-a", "your-redis-password-here", "ping"] # CHANGE THIS TO YOUR REDIS PASSWORD interval: 3s @@ -79,6 +83,8 @@ services: volumes: - agent_files:/app/agents - branding_assets:/app/assets + networks: + - patchmon-internal depends_on: database: condition: service_healthy @@ -92,6 +98,8 @@ services: - "3000:3000" volumes: - branding_assets:/usr/share/nginx/html/assets + networks: + - patchmon-internal depends_on: backend: condition: service_healthy @@ -101,3 +109,7 @@ volumes: redis_data: agent_files: branding_assets: + +networks: + patchmon-internal: + driver: bridge