Skip to main content

Container Networking

Container networking is the core infrastructure that controls communication between containers in a Docker environment. Proper network design simultaneously achieves security (preventing unnecessary external port exposure), stability (isolation between services), and maintainability (DNS-based service discovery). This chapter systematically covers everything from Docker network types to practical multi-network configurations and debugging techniques.


Docker Network Types​

Docker provides various network drivers, and you should choose the appropriate driver based on your use case.

DriverEnvironmentDescription
bridgeSingle host (default)Creates virtual bridge (docker0) on host, isolated inter-container communication
hostSingle host (high performance)Container directly shares host network stack, no isolation
overlayMulti-host (Swarm/K8s)Distributed network spanning multiple Docker hosts
noneComplete isolationNo network interface, no external communication possible
macvlanLegacy integrationAssigns real MAC address to container, connects directly to physical network

bridge Network (Default)​

The most common driver for inter-container communication on a single Docker host.

Host OS
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”‚
β”‚ docker0 (bridge: 172.17.0.1) β”‚
β”‚ β”‚ β”‚
β”‚ β”œβ”€β”€ nginx (172.17.0.2) β”‚
β”‚ β”œβ”€β”€ app (172.17.0.3) β”‚
β”‚ └── db (172.17.0.4) β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Characteristics of the default bridge network:

  • Containers can communicate with each other via IP address
  • Default bridge (docker0) does not support DNS name resolution (must use IP directly)
  • User-defined bridge networks support automatic DNS resolution by container name
# Check default bridge network
docker network ls

# Detailed info for default bridge (docker0)
docker network inspect bridge

host Network​

The container directly uses the host's network stack. No port mapping is needed and performance is best, but there is no isolation, making it vulnerable to security issues.

services:
app:
image: myapp:latest
network_mode: host # Directly use host network

none Network​

A state where network interfaces are completely removed. Used for batch jobs like file processing or encryption that require absolutely no external communication.

docker run --network none myapp:latest

Key Advantages of User-Defined bridge Networks​

Networks created by Docker Compose are all user-defined bridge networks. They provide important additional features compared to the default docker0 bridge.

Automatic DNS Name Resolution​

In user-defined bridge networks, a container's service name becomes its DNS hostname.

# If the container name is 'db', it's accessible as 'db' internally
# Spring Boot application.properties example:
# spring.datasource.url=jdbc:postgresql://db:5432/myapp_db
# ↑↑
# Container service name = DNS

# In Nginx upstream:
# upstream spring_app {
# server app:8080; ← 'app' service is automatically resolved to IP
# }

Per-Network Isolation​

Containers belonging to different networks cannot communicate. A single container can participate in multiple networks simultaneously.


Docker Compose Automatic Network Creation​

When no networks section is defined, Docker Compose automatically creates a default network prefixed with the project name and connects all services to it.

# Without networks section β†’ {project}_default is auto-created
services:
nginx:
image: nginx:alpine
app:
image: myapp:latest
db:
image: postgres:16-alpine
# If project name is 'myproject':
# myproject_default network auto-created
# All services connected to the same network

docker network ls
# NETWORK ID NAME DRIVER SCOPE
# abc123 myproject_default bridge local

Disadvantage of auto-created networks: all services share the same network, making the DB accessible from external sources. Explicit multi-network configuration is recommended.


Multi-Network Configuration: Separating frontend / backend​

In real production environments, separate networks to completely block unnecessary communication.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Host OS β”‚
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ frontend network (bridge) β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
β”‚ β”‚ β”‚ Nginx │───────►│ Spring Boot β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ :80, :443 β”‚ β”‚ :8080 β”‚ β”‚ β”‚
β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚ β”‚ β–² β”‚ β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ Port exposure β”‚ β”‚
β”‚ External traffic β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ backend network (bridge) β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
β”‚ β”‚ β”‚ Spring Boot β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ :8080 β”‚ β”‚ β”‚
β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
β”‚ β”‚ β”‚ PostgreSQL β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ :5432 β”‚ β”‚ β”‚
β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
version: '3.9'

services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
networks:
- frontend # Participates in frontend network only

app:
build: .
expose:
- "8080"
networks:
- frontend # Communicates with Nginx
- backend # Communicates with DB

db:
image: postgres:16-alpine
expose:
- "5432"
networks:
- backend # Participates in backend network only (no external access)

networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # Containers on this network cannot access external internet

Core isolation rules:

  • Nginx cannot directly access DB (not on the same network)
  • DB cannot access external internet (internal: true)
  • DB port (5432) is completely inaccessible from outside (expose only, no ports)

ports vs expose Difference​

Both directives "open" ports, but the scope differs.

DirectiveScopePurpose
portsHost ↔ ContainerAccessible from outside, binds host port
exposeBetween containers (internal)Communication within container network only
# ports: accessible from outside via host port 8080
ports:
- "8080:8080" # host:container

# expose: accessible only from other containers on the same network
expose:
- "8080" # Not accessible from outside

Practical principles:

  • Nginx: expose 80, 443 externally via ports
  • Spring Boot, DB: use expose only, block external access

Service Discovery (DNS)​

In user-defined networks, Docker automatically converts container names to IP addresses through its built-in DNS server.

# DNS verification from inside Spring Boot container
docker compose exec app ping db # 'db' service name resolved to IP
docker compose exec app nslookup db # Check DNS query result

# DNS verification for app service from Nginx container
docker compose exec nginx ping app
docker compose exec nginx wget -O- http://app:8080/actuator/health

DNS resolution rules:

  • service name β†’ internal IP of that container
  • When scaled up (--scale app=3) β†’ automatic load balancing via DNS round-robin
  • Even if IP changes due to container restart, DNS auto-updates

overlay Network (Docker Swarm)​

Used in Docker Swarm environments that deploy containers across multiple Docker hosts. Containers on physically different servers can communicate as if they're on the same network.

# Initialize Swarm
docker swarm init --advertise-addr 192.168.1.10

# Join worker node
docker swarm join --token <token> 192.168.1.10:2377

# Create overlay network
docker network create \
--driver overlay \
--attachable \
my-overlay-network
# docker-compose.yml (for Swarm deployment)
version: '3.9'

services:
nginx:
image: nginx:alpine
networks:
- overlay-network
deploy:
replicas: 2
placement:
constraints:
- node.role == worker

networks:
overlay-network:
driver: overlay
attachable: true

Overlay networks use VXLAN tunneling to connect containers on different hosts. For single hosts, bridge networks are simpler and perform better.


Network Debugging​

docker network inspect​

View detailed network information (connected containers, IP ranges, driver configuration, etc.).

# List all networks
docker network ls

# Detailed info for a specific network
docker network inspect myproject_frontend

# Check connected containers and IPs
docker network inspect myproject_backend --format \
'{{range .Containers}}{{.Name}}: {{.IPv4Address}}{{println}}{{end}}'

Connection Testing via docker exec​

# Ping another container from inside a container
docker compose exec nginx ping app
docker compose exec nginx ping db # nginx (only in frontend) cannot access db

# HTTP connection test with curl
docker compose exec nginx curl -v http://app:8080/actuator/health

# Check open ports with netstat
docker compose exec app netstat -tlnp

# Trace route with traceroute
docker compose exec app traceroute db

Systematic Diagnosis of Network Connectivity Issues​

# 1. Verify containers are running
docker compose ps

# 2. Check if they belong to the same network
docker network inspect myproject_backend

# 3. Check firewall rules (host)
sudo iptables -L -n | grep DOCKER

# 4. Check connection errors in container logs
docker compose logs app | grep -i "connection refused\|timeout\|refused"

# 5. Verify DNS resolution
docker compose exec app nslookup db
docker compose exec app cat /etc/resolv.conf

Security Perspective: Preventing Unnecessary External Port Exposure​

Checklist​

# Check currently open ports on host (external exposure status)
netstat -tlnp | grep docker
# or
ss -tlnp | grep docker

Incorrect configuration (security risk):

# ❌ Directly exposing DB port to host - absolutely forbidden
db:
image: postgres:16-alpine
ports:
- "5432:5432" # DB directly accessible from outside!

Correct configuration:

# βœ… Use expose only to allow internal container communication
db:
image: postgres:16-alpine
expose:
- "5432" # Only accessible within container network
networks:
- backend # Participates in backend network only

Additional security hardening:

# When you need temporary direct DB access in dev:
# docker-compose.override.yml (dev only)
services:
db:
ports:
- "127.0.0.1:5432:5432" # Localhost only (never use 0.0.0.0)

0.0.0.0:5432:5432 is accessible from all interfaces, so always restrict to localhost with 127.0.0.1:5432:5432.


Expert Tips​

1. Explicitly specify network names

networks:
frontend:
name: myapp-frontend # Specify actual Docker network name (no project prefix)
backend:
name: myapp-backend
internal: true

2. Reuse existing external networks

When multiple Docker Compose projects need to share the same network:

networks:
shared-network:
external: true # Use an already existing network
name: myapp-frontend

3. IPv6 support

networks:
frontend:
driver: bridge
enable_ipv6: true
ipam:
config:
- subnet: "2001:db8::/64"

4. Resolving network range conflicts

When company internal network and Docker ranges conflict:

networks:
frontend:
driver: bridge
ipam:
config:
- subnet: 172.30.0.0/24 # Specify custom subnet
gateway: 172.30.0.1