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.
This commit is contained in:
Contributor
2026-01-03 18:01:30 +00:00
committed by Muhammad Ibrahim
parent dd09b82299
commit 0e58b7bea1
3 changed files with 97 additions and 0 deletions

View File

@@ -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 <container> ping backend`
---
# Development
This section is for developers who want to contribute to PatchMon or run it in development mode.

View File

@@ -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

View File

@@ -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