docker.recipes
Networking8 min read

Docker Network Isolation Patterns

Use multiple Docker networks to isolate services, protect databases, and implement defense in depth.

01Why Network Isolation Matters

By default, all containers in a Docker Compose project can communicate with each other. This is convenient but insecure—if one container is compromised, it can potentially attack others. **Isolation benefits:** • Limit blast radius of compromises • Databases only accessible by their apps • Public-facing services separated from internal • Follows principle of least privilege **Common patterns:** • Frontend/backend separation • Database isolation • Reverse proxy networks • Management network separation

Think of Docker networks like VLANs. Services on different networks can't communicate unless you explicitly connect them to shared networks.

02Basic Two-Network Pattern

The simplest isolation pattern uses two networks: 'frontend' for public-facing services and 'backend' for internal services. The reverse proxy bridges both. **Traffic flow:** Internet → Reverse Proxy (frontend) → App (frontend+backend) → Database (backend only) The database is never directly reachable from the internet because it's only on the backend network.
1services:
2 traefik:
3 image: traefik:v3.0
4 ports:
5 - "80:80"
6 - "443:443"
7 networks:
8 - frontend # Connects to public-facing services
9 volumes:
10 - /var/run/docker.sock:/var/run/docker.sock:ro
11
12 app:
13 image: myapp:latest
14 networks:
15 - frontend # Reachable by traefik
16 - backend # Can reach database
17 labels:
18 - "traefik.enable=true"
19 - "traefik.http.routers.app.rule=Host(`app.example.com`)"
20
21 database:
22 image: postgres:16
23 networks:
24 - backend # Only reachable by app
25 volumes:
26 - db_data:/var/lib/postgresql/data
27 environment:
28 - POSTGRES_PASSWORD=${DB_PASSWORD}
29 # No ports exposed!
30
31networks:
32 frontend:
33 backend:
34
35volumes:
36 db_data:

03Database Isolation Pattern

For multi-service architectures, give each application its own database network. This prevents one compromised app from accessing another app's database. **Pattern:** • Each app + its database share a private network • Apps join the proxy network for external access • Databases are completely isolated from each other
1services:
2 # Reverse proxy - public network only
3 traefik:
4 image: traefik:v3.0
5 networks:
6 - proxy
7
8 # App 1 with its own database
9 nextcloud:
10 image: nextcloud:latest
11 networks:
12 - proxy
13 - nextcloud_internal
14 depends_on:
15 - nextcloud_db
16
17 nextcloud_db:
18 image: mariadb:11
19 networks:
20 - nextcloud_internal # Only nextcloud can reach this
21 volumes:
22 - nextcloud_db:/var/lib/mysql
23 environment:
24 - MYSQL_ROOT_PASSWORD=${NC_DB_ROOT_PASS}
25
26 # App 2 with its own database
27 gitea:
28 image: gitea/gitea:latest
29 networks:
30 - proxy
31 - gitea_internal
32 depends_on:
33 - gitea_db
34
35 gitea_db:
36 image: postgres:16
37 networks:
38 - gitea_internal # Only gitea can reach this
39 volumes:
40 - gitea_db:/var/lib/postgresql/data
41
42networks:
43 proxy:
44 nextcloud_internal:
45 gitea_internal:
46
47volumes:
48 nextcloud_db:
49 gitea_db:

Name your networks descriptively. 'nextcloud_internal' is clearer than 'net1' when debugging.

04Management Network Pattern

Monitoring and management tools need access to many services but shouldn't be publicly accessible. Create a dedicated management network. **Management services:** • Prometheus (metrics collection) • Grafana (dashboards) • Portainer (container management) • Log aggregators These join the management network alongside the services they monitor, but don't join the public proxy network (or are protected by authentication).
1services:
2 # Public-facing app
3 app:
4 image: myapp:latest
5 networks:
6 - proxy
7 - management # Exposes metrics
8 labels:
9 - "traefik.enable=true"
10
11 # Metrics collection - not public
12 prometheus:
13 image: prom/prometheus:latest
14 networks:
15 - management
16 volumes:
17 - ./prometheus.yml:/etc/prometheus/prometheus.yml
18 - prometheus_data:/prometheus
19 # No traefik labels - not publicly accessible
20
21 # Dashboards - protected access
22 grafana:
23 image: grafana/grafana:latest
24 networks:
25 - proxy # For external access (with auth)
26 - management # To reach prometheus
27 labels:
28 - "traefik.enable=true"
29 - "traefik.http.routers.grafana.rule=Host(`grafana.example.com`)"
30 # Add authentication middleware here
31
32networks:
33 proxy:
34 management:
35
36volumes:
37 prometheus_data:

05Network Aliases for Service Discovery

When a container is on multiple networks, it might need different names on each network. Use network aliases to control how services discover each other. **Use cases:** • Same container, different roles on different networks • Migration: old name on one network, new name on another • Multiple services behind one container
1services:
2 # API server known by different names
3 api:
4 image: myapi:latest
5 networks:
6 frontend:
7 aliases:
8 - api
9 - api.internal
10 backend:
11 aliases:
12 - api-server
13 - api.backend.local
14
15 # Consumer on frontend network
16 web:
17 image: myweb:latest
18 networks:
19 - frontend
20 environment:
21 - API_URL=http://api:8080 # Uses frontend alias
22
23 # Worker on backend network
24 worker:
25 image: myworker:latest
26 networks:
27 - backend
28 environment:
29 - API_URL=http://api-server:8080 # Uses backend alias
30
31networks:
32 frontend:
33 backend:

Aliases are per-network. The same container can be 'db' on one network and 'postgres-primary' on another.

06External Networks for Cross-Project Communication

When services in different Compose projects need to communicate, use external networks. Create the network once, reference it from multiple projects. **Common scenarios:** • Shared reverse proxy serving multiple projects • Centralized monitoring for all stacks • Shared database cluster
1# Create shared networks once
2docker network create proxy
3docker network create monitoring

07Using External Networks

Reference the external networks in your Compose files. Services on the same external network can communicate across projects.
1# Project 1: Traefik (reverse proxy)
2services:
3 traefik:
4 image: traefik:v3.0
5 networks:
6 - proxy
7 # ...
8
9networks:
10 proxy:
11 external: true
12
13---
14
15# Project 2: Web app
16services:
17 webapp:
18 image: mywebapp:latest
19 networks:
20 - proxy # Reachable by traefik
21 - internal # App's own internal network
22 labels:
23 - "traefik.enable=true"
24
25 database:
26 networks:
27 - internal # Not on proxy network
28
29networks:
30 proxy:
31 external: true
32 internal:
33 # This one is project-local

External networks persist even when you 'docker compose down'. Delete them explicitly with 'docker network rm' when no longer needed.

08Debugging Network Issues

When services can't communicate, use these commands to debug network configuration: **Common issues:** • Services on different networks • Typo in network names • External network doesn't exist • Wrong port or hostname
1# List all networks
2docker network ls
3
4# Inspect a network (shows connected containers)
5docker network inspect proxy
6
7# See which networks a container is on
8docker inspect mycontainer --format='{{json .NetworkSettings.Networks}}' | jq
9
10# Test connectivity from inside a container
11docker exec webapp ping database
12docker exec webapp nc -zv database 5432
13
14# Check DNS resolution
15docker exec webapp nslookup database
16docker exec webapp cat /etc/hosts
17
18# Temporarily connect container to network for debugging
19docker network connect proxy mycontainer
20docker network disconnect proxy mycontainer

Use 'docker compose config' to see the fully resolved Compose file, including generated network names.