Docker Compose Networking
Master network configuration and service communication patterns in Docker Compose for building secure, scalable multi-container applications.
Default Bridge Network
Docker Compose automatically creates a default bridge network for all services in a compose file:
version: '3.8'
services: frontend: image: nginx:alpine ports: - "80:80" # Can reach backend using service name as hostname
backend: image: node:18-alpine command: node server.js # Accessible at hostname 'backend' from frontend
database: image: postgres:15-alpine environment: POSTGRES_PASSWORD: secret # Accessible at hostname 'database' from backend
In this setup:
All services can communicate using service names as hostnames Frontend can reach backend at http://backend:3000 Backend can reach database at postgres://database:5432 Only frontend's port 80 is exposed to host Custom Bridge Networks
Define custom networks for service isolation and segmentation:
version: '3.8'
services: frontend: image: nginx:alpine networks: - frontend-network ports: - "80:80"
api: image: node:18-alpine networks: - frontend-network - backend-network environment: DATABASE_URL: postgresql://db:5432/app
database: image: postgres:15-alpine networks: - backend-network environment: POSTGRES_PASSWORD: secret POSTGRES_DB: app volumes: - db-data:/var/lib/postgresql/data
cache: image: redis:7-alpine networks: - backend-network command: redis-server --appendonly yes volumes: - redis-data:/data
networks: frontend-network: driver: bridge backend-network: driver: bridge internal: true
volumes: db-data: redis-data:
Network isolation:
Frontend can only reach API Frontend cannot reach database or cache directly API can reach all services Backend network is internal (no external access) Network Aliases
Configure multiple hostnames for service discovery:
version: '3.8'
services: web: image: nginx:alpine networks: public: aliases: - website - www - web-server internal: aliases: - web-internal
api: image: node:18-alpine networks: public: aliases: - api-server - backend internal: aliases: - api-internal depends_on: - database
database: image: postgres:15-alpine networks: internal: aliases: - db - postgres - primary-db environment: POSTGRES_PASSWORD: secret
networks: public: driver: bridge internal: driver: bridge internal: true
Services can be reached by any of their aliases:
http://website, http://www, http://web-server all reach web service postgresql://db:5432, postgresql://postgres:5432 both reach database Static IP Addresses
Assign fixed IP addresses for services requiring stable networking:
version: '3.8'
services: loadbalancer: image: nginx:alpine networks: app-network: ipv4_address: 172.28.1.10 ports: - "80:80" volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro
app-1: image: myapp:latest networks: app-network: ipv4_address: 172.28.1.11 environment: APP_ID: "1"
app-2: image: myapp:latest networks: app-network: ipv4_address: 172.28.1.12 environment: APP_ID: "2"
app-3: image: myapp:latest networks: app-network: ipv4_address: 172.28.1.13 environment: APP_ID: "3"
database: image: postgres:15-alpine networks: app-network: ipv4_address: 172.28.1.20 environment: POSTGRES_PASSWORD: secret volumes: - pgdata:/var/lib/postgresql/data
networks: app-network: driver: bridge ipam: driver: default config: - subnet: 172.28.0.0/16 gateway: 172.28.1.1
volumes: pgdata:
External Networks
Connect to existing Docker networks created outside Compose:
version: '3.8'
services: api: image: node:18-alpine networks: - app-network - shared-network environment: DATABASE_URL: postgresql://db:5432/app
database: image: postgres:15-alpine networks: - app-network environment: POSTGRES_PASSWORD: secret volumes: - pgdata:/var/lib/postgresql/data
networks: app-network: driver: bridge
shared-network: external: true name: company-shared-network
volumes: pgdata:
Create external network first:
docker network create company-shared-network docker compose up -d
Host Network Mode
Use host networking for maximum performance (Linux only):
version: '3.8'
services: high-performance-app: image: myapp:latest network_mode: "host" environment: BIND_ADDRESS: "0.0.0.0" PORT: "8080" # No port mapping needed - directly uses host's network stack
monitoring: image: prometheus:latest network_mode: "host" volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro - prometheus-data:/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' - '--storage.tsdb.path=/prometheus' - '--web.listen-address=0.0.0.0:9090'
volumes: prometheus-data:
Note: Host networking bypasses Docker network isolation and is typically used for monitoring tools or high-throughput applications.
Service Discovery and DNS
Configure DNS resolution and service discovery:
version: '3.8'
services: api: image: node:18-alpine networks: - app-network dns: - 8.8.8.8 - 8.8.4.4 dns_search: - company.local extra_hosts: - "legacy-api.company.local:192.168.1.100" - "auth-service.company.local:192.168.1.101" environment: DATABASE_HOST: database.company.local
database: image: postgres:15-alpine networks: app-network: aliases: - database.company.local - db.company.local hostname: primary-database domainname: company.local environment: POSTGRES_PASSWORD: secret volumes: - pgdata:/var/lib/postgresql/data
networks: app-network: driver: bridge driver_opts: com.docker.network.bridge.name: br-company-app
volumes: pgdata:
Link Containers (Legacy)
While links is deprecated, understanding it helps migrate legacy configurations:
version: '3.8'
services: # Modern approach - use networks instead web: image: nginx:alpine networks: - app-network depends_on: - api
api: image: node:18-alpine networks: - app-network depends_on: - database environment: # Use service name as hostname DATABASE_URL: postgresql://database:5432/app
database: image: postgres:15-alpine networks: - app-network environment: POSTGRES_PASSWORD: secret
networks: app-network: driver: bridge
Multi-Network Architecture
Complex applications with multiple isolated networks:
version: '3.8'
services: nginx: image: nginx:alpine networks: - public ports: - "80:80" - "443:443" volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro - ./ssl:/etc/nginx/ssl:ro depends_on: - frontend - api
frontend: image: react-app:latest networks: - public - frontend-tier environment: API_URL: http://api:3000
api: image: node-api:latest networks: - frontend-tier - backend-tier environment: DATABASE_URL: postgresql://postgres:5432/app REDIS_URL: redis://cache:6379 QUEUE_URL: amqp://rabbitmq:5672 depends_on: - database - cache - queue
worker: image: node-worker:latest networks: - backend-tier environment: DATABASE_URL: postgresql://postgres:5432/app QUEUE_URL: amqp://rabbitmq:5672 depends_on: - database - queue deploy: replicas: 3
database: image: postgres:15-alpine networks: - backend-tier environment: POSTGRES_PASSWORD: secret POSTGRES_DB: app volumes: - pgdata:/var/lib/postgresql/data
cache: image: redis:7-alpine networks: - backend-tier command: redis-server --appendonly yes volumes: - redis-data:/data
queue: image: rabbitmq:3-management-alpine networks: - backend-tier - management ports: - "15672:15672" # Management UI environment: RABBITMQ_DEFAULT_USER: admin RABBITMQ_DEFAULT_PASS: secret volumes: - rabbitmq-data:/var/lib/rabbitmq
monitoring: image: prometheus:latest networks: - management ports: - "9090:9090" volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro - prometheus-data:/prometheus
networks: public: driver: bridge frontend-tier: driver: bridge internal: true backend-tier: driver: bridge internal: true management: driver: bridge
volumes: pgdata: redis-data: rabbitmq-data: prometheus-data:
Network segmentation:
Public: Internet-facing services (nginx, frontend) Frontend-tier: Frontend and API communication Backend-tier: API, workers, databases, cache, queue Management: Monitoring and administration tools Port Publishing Strategies
Control how services expose ports:
version: '3.8'
services: # Short syntax - host:container web: image: nginx:alpine ports: - "80:80" - "443:443" networks: - public
# Long syntax with protocol specification api: image: node:18-alpine ports: - target: 3000 published: 3000 protocol: tcp mode: host networks: - app-network
# Random host port app: image: myapp:latest ports: - "3000" # Docker assigns random host port networks: - app-network
# Bind to specific host interface admin: image: admin-panel:latest ports: - "127.0.0.1:8080:80" # Only accessible from localhost networks: - admin-network
# UDP protocol dns: image: bind9:latest ports: - "53:53/udp" - "53:53/tcp" networks: - dns-network
# Range of ports streaming: image: rtmp-server:latest ports: - "1935:1935" - "8080-8089:8080-8089" networks: - streaming-network
networks: public: app-network: admin-network: internal: true dns-network: streaming-network:
Container Communication Patterns Request-Response Pattern version: '3.8'
services: gateway: image: nginx:alpine networks: - frontend ports: - "80:80" volumes: - ./nginx-gateway.conf:/etc/nginx/nginx.conf:ro
service-a: image: service-a:latest networks: - frontend - backend environment: SERVICE_B_URL: http://service-b:8080 DATABASE_URL: postgresql://db:5432/service_a
service-b: image: service-b:latest networks: - frontend - backend environment: DATABASE_URL: postgresql://db:5432/service_b
database: image: postgres:15-alpine networks: - backend environment: POSTGRES_PASSWORD: secret volumes: - pgdata:/var/lib/postgresql/data
networks: frontend: driver: bridge backend: driver: bridge internal: true
volumes: pgdata:
Pub-Sub Pattern version: '3.8'
services: publisher: image: publisher:latest networks: - messaging environment: REDIS_URL: redis://redis:6379 depends_on: - redis
subscriber-1: image: subscriber:latest networks: - messaging environment: REDIS_URL: redis://redis:6379 SUBSCRIBER_ID: "1" depends_on: - redis
subscriber-2: image: subscriber:latest networks: - messaging environment: REDIS_URL: redis://redis:6379 SUBSCRIBER_ID: "2" depends_on: - redis
redis: image: redis:7-alpine networks: - messaging command: redis-server --appendonly yes volumes: - redis-data:/data
networks: messaging: driver: bridge driver_opts: com.docker.network.bridge.enable_icc: "true"
volumes: redis-data:
Network Troubleshooting Configuration
Enable debugging and monitoring:
version: '3.8'
services: app: image: myapp:latest networks: app-network: aliases: - primary-app cap_add: - NET_ADMIN - NET_RAW
debug: image: nicolaka/netshoot:latest networks: - app-network command: sleep infinity cap_add: - NET_ADMIN - NET_RAW stdin_open: true tty: true
database: image: postgres:15-alpine networks: app-network: aliases: - db environment: POSTGRES_PASSWORD: secret
networks: app-network: driver: bridge driver_opts: com.docker.network.bridge.enable_ip_masquerade: "true" com.docker.network.driver.mtu: "1500" ipam: driver: default config: - subnet: 172.28.0.0/16
Debug commands:
Enter debug container
docker compose exec debug bash
Test connectivity
ping app curl http://app:8080/health
Check DNS resolution
nslookup app dig app
Network scanning
nmap -p- app
Trace route
traceroute app
Monitor traffic
tcpdump -i eth0 -n
IPv6 Networking
Enable IPv6 support:
version: '3.8'
services: web: image: nginx:alpine networks: - ipv6-network ports: - "80:80"
api: image: node:18-alpine networks: ipv6-network: ipv6_address: 2001:db8:1::10
database: image: postgres:15-alpine networks: ipv6-network: ipv6_address: 2001:db8:1::20 environment: POSTGRES_PASSWORD: secret
networks: ipv6-network: driver: bridge enable_ipv6: true ipam: driver: default config: - subnet: 172.28.0.0/16 - subnet: 2001:db8:1::/64
When to Use This Skill
Use docker-compose-networking when you need to:
Configure custom network topologies for multi-container applications Implement network segmentation and service isolation Set up service discovery and inter-service communication Design secure network architectures with frontend/backend separation Configure static IP addresses for services Connect to external Docker networks Implement complex microservices communication patterns Troubleshoot network connectivity issues Configure DNS resolution and hostname aliases Set up pub-sub or message queue architectures Enable IPv6 networking Optimize network performance with host networking Configure port publishing and exposure strategies Best Practices
Use Custom Networks for Isolation: Always create custom networks instead of relying solely on the default network for better security and organization.
Implement Network Segmentation: Separate frontend, backend, and data tiers into different networks to limit attack surface.
Use Internal Networks: Mark backend networks as internal: true to prevent external access to sensitive services like databases.
Prefer Service Names Over IPs: Use Docker's built-in DNS and service names instead of hardcoding IP addresses for maintainability.
Configure Health Checks: Implement health checks to ensure services are ready before routing traffic to them.
Use Network Aliases: Define meaningful aliases for services to support multiple naming conventions and easier migration.
Avoid Host Networking Unless Necessary: Use bridge networks by default; host networking should only be used for specific performance requirements.
Document Network Architecture: Clearly comment your network design and document which services can communicate with each other.
Use Depends_on Wisely: Combine depends_on with health checks to ensure services start in the correct order.
Implement Least Privilege: Only expose ports that absolutely need to be accessible from outside the Docker network.
Use Environment Variables for URLs: Configure service endpoints through environment variables for flexibility across environments.
Test Network Isolation: Regularly verify that services can only communicate through intended network paths.
Configure Appropriate MTU: Set MTU values based on your network infrastructure to avoid fragmentation issues.
Use External Networks for Shared Resources: When multiple Compose projects need to communicate, use external networks rather than duplicating services.
Monitor Network Performance: Use tools like docker stats and dedicated monitoring containers to track network usage and identify bottlenecks.
Common Pitfalls
Exposing All Services Publicly: Don't publish ports for services that should only be accessed internally; use networks instead of port publishing.
Hardcoding IP Addresses: Avoid static IP addresses unless absolutely necessary; rely on service discovery instead.
Using Default Network Only: Not creating custom networks misses opportunities for proper segmentation and isolation.
Ignoring Network Modes: Using the wrong network mode (bridge vs host vs overlay) for your use case can cause connectivity or performance issues.
Missing Network Dependencies: Not properly configuring depends_on can cause services to fail when trying to connect to unavailable services.
Overusing Host Networking: Using network_mode: host unnecessarily breaks container isolation and portability.
Not Using Internal Networks: Failing to mark backend networks as internal leaves databases and sensitive services exposed.
Mixing Network Modes: Trying to publish ports or connect to networks when using network_mode: host causes configuration errors.
Circular Network Dependencies: Creating network dependencies that form a circle prevents containers from starting properly.
Ignoring DNS Configuration: Not configuring DNS properly can cause name resolution failures in containerized applications.
Subnet Conflicts: Using IP ranges that conflict with host or other Docker networks causes routing issues.
Not Testing Network Policies: Assuming network isolation works without testing can leave security vulnerabilities.
Exposing Management Interfaces: Publishing management ports (like RabbitMQ, Redis, PostgreSQL) without authentication or IP restrictions.
Using Links Instead of Networks: The deprecated links feature should be replaced with modern network configuration.
Ignoring Network Driver Options: Not configuring driver options like MTU or IP masquerade can cause subtle connectivity problems in production.
Resources Official Documentation Docker Compose Networking Docker Network Drivers Docker DNS Network Troubleshooting Nicolaka Netshoot - Network troubleshooting container Docker Network Inspect Architecture Patterns Microservices Network Patterns Docker Security Best Practices Tools Docker Network Commands Compose Network Reference