Docker networking controls how containers communicate with each other, the host, and external networks through five pluggable drivers — bridge, host, overlay, macvlan, and none. Pick the wrong driver and you get DNS that silently returns the wrong container, port conflicts with the corporate VPN, or 30% throughput loss on hot paths. The Compose default network model has been stable since Compose v2 in 2021, but Docker Engine 25 (2024) shipped IPv6 by default on user-defined bridges and tightened the embedded DNS server at 127.0.0.11. For day-to-day commands, keep the Docker cheat sheet open in another tab.
What Are the Docker Network Drivers?
Docker uses pluggable network drivers. The default is bridge, which creates an isolated virtual network on the host. Each driver has different isolation, performance, and discoverability characteristics.
| Driver | Scope | DNS | Use Case |
|---|---|---|---|
| bridge | Single host | User-defined only | Default — isolated containers on one host |
| host | Single host | Host's DNS | No network isolation — max performance |
| overlay | Multi-host | Yes | Swarm services across multiple nodes |
| none | N/A | No | Completely isolated — no networking |
| macvlan | Single host | No | Assign a MAC address — appear as physical device |
host networking eliminates the per-packet NAT overhead Docker imposes on bridge — measured around a 5–10% throughput gain on 10 Gbps interfaces and roughly 30 µs latency reduction on small RPCs. The trade-off is zero network isolation: the container sees the host's interfaces directly, and a port conflict crashes the container.
How Does Bridge Networking Work?
The default bridge network (docker0) provides basic connectivity but no automatic DNS resolution between containers. Always create a user-defined bridge network instead — it provides DNS, better isolation, and the ability to connect/disconnect containers at runtime.
# Create a user-defined bridge network
docker network create app-network
# Run containers on the network
docker run -d --name api --network app-network node:22-slim
docker run -d --name db --network app-network postgres:17
# The 'api' container can now reach 'db' by name
# From inside 'api': ping db → resolves to the container's IP
# Inspect the network (see connected containers, subnet, gateway)
docker network inspect app-network
# Disconnect a container from the network
docker network disconnect app-network api
# Remove the network (must disconnect all containers first)
docker network rm app-networkDefault vs user-defined bridge: The default docker0 bridge requires --link for container name resolution (deprecated). User-defined bridges have automatic DNS, configurable subnets, and better isolation between groups of containers.
How Does Port Mapping Work?
Port mapping publishes container ports to the host using -p. Without it, container ports are only accessible from within the Docker network. This is also how the Dev Containers spec exposes a service running inside a workspace container to the editor on the host.
# Map host port 8080 → container port 3000
docker run -p 8080:3000 myapp
# Map to a specific host interface (localhost only)
docker run -p 127.0.0.1:8080:3000 myapp
# Map a range of ports
docker run -p 8000-8010:8000-8010 myapp
# Let Docker pick a random host port
docker run -p 3000 myapp
# Check assigned port: docker port <container_id>
# UDP port mapping
docker run -p 5353:53/udp dns-server
# Multiple port mappings
docker run -p 80:80 -p 443:443 nginxHow Does DNS Resolution Work in Docker?
On user-defined networks, Docker runs an embedded DNS server at 127.0.0.11. Containers can resolve each other by container name or network alias. This is the foundation of service discovery in Docker.
# Container name = DNS name (on user-defined networks)
docker run -d --name redis --network app-network redis:7
# From another container on the same network:
# redis:6379 resolves to the container's IP
# Network aliases — multiple DNS names for one container
docker run -d --name redis-primary \
--network app-network \
--network-alias redis \
--network-alias cache \
redis:7
# Both 'redis' and 'cache' resolve to this container
# Debug DNS resolution
docker run --rm --network app-network alpine nslookup redisHow do containers connect to the host?
The classic mistake: curl http://127.0.0.1:5432 from inside a container hits the container's loopback, not the host's. Reaching a service on the host machine depends on the platform.
- Docker Desktop (Mac, Windows): use the magic hostname
host.docker.internal. Docker resolves it to the host's IP from inside the VM. Works out of the box. - Linux: there is no built-in
host.docker.internal. Add it explicitly with--add-host=host.docker.internal:host-gateway(Docker 20.10+, Aug 2020), or fall back to the bridge gateway IP172.17.0.1. - Network namespace shortcut:
--network hostbypasses the question entirely — the container is already on the host's stack.
# Mac/Windows — already works
docker run --rm alpine ping -c 1 host.docker.internal
# Linux — opt in to the same hostname
docker run --rm \
--add-host=host.docker.internal:host-gateway \
alpine ping -c 1 host.docker.internal
# Linux fallback — bridge gateway IP
docker run --rm alpine sh -c 'apk add --no-cache curl && curl http://172.17.0.1:5432'How Does Docker Compose Networking Work?
Docker Compose automatically creates a network for each project. All services in a compose.yaml can reach each other by service name. You can also define custom networks to isolate groups of services.
services:
api:
image: node:22-slim
ports:
- "3000:3000"
networks:
- frontend
- backend
depends_on:
- db
- redis
web:
image: nginx:latest
ports:
- "80:80"
networks:
- frontend
# 'web' can reach 'api' but NOT 'db' or 'redis'
db:
image: postgres:17
environment:
POSTGRES_PASSWORD: secret
networks:
- backend
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # No external access — db/redis isolated
volumes:
pgdata:How Does Multi-Host Networking Work?
Overlay networks span multiple Docker hosts, enabling containers on different machines to communicate as if on the same LAN. This is used in Docker Swarm and can be attached to standalone containers with the --attachable flag. Most teams running Swarm in 2026 are quietly migrating to Kubernetes — see the Docker vs Kubernetes comparison and the Kubernetes cheat sheet for the equivalent CNI-driven model.
# Initialize Swarm (required for overlay networks)
docker swarm init
# Create an attachable overlay network
docker network create -d overlay --attachable multi-host-net
# Deploy a service across multiple nodes
docker service create \
--name api \
--network multi-host-net \
--replicas 3 \
myapp:latest
# Standalone containers can also join (with --attachable)
docker run -d --name debug --network multi-host-net alpine sleep 3600
# Inspect overlay network
docker network inspect multi-host-netWhat are the common Docker networking pitfalls?
- Default subnet collides with corporate VPN. Docker's default bridge sits at
172.17.0.0/16, which routinely overlaps with VPN or data-centre ranges. Pick a quiet block when you create the network:docker network create --subnet 10.42.0.0/16 app-net. - Overlay MTU is 1450, not 1500. VXLAN encapsulation eats 50 bytes. If you see TLS handshakes that succeed but large responses hang, set
--opt com.docker.network.driver.mtu=1450on the overlay (or lower if your underlay also tunnels). expose≠portsin Compose.exposeis documentation-only metadata for inter-container access; it does not publish a port to the host. Onlyportsopens a host-side mapping.- iptables rules persist after Docker restarts. Docker writes rules into the
DOCKERchain on daemon start; manual edits to those chains are wiped, and stale rules from earlier containers can survive crashes. Reset withsystemctl restart dockerrather than hand-editing. 127.0.0.1inside the container is not the host. It's the container's own loopback. Usehost.docker.internal(Mac/Windows or Linux with--add-host) to talk to a service on the host.
How Do You Debug Docker Networking Issues?
When containers cannot reach each other, use these commands to diagnose the issue.
# List all networks
docker network ls
# Inspect a network (shows connected containers, IPs, subnet)
docker network inspect app-network
# Check which networks a container is connected to
docker inspect --format='{{json .NetworkSettings.Networks}}' my-container
# Test connectivity from inside a container
docker exec -it api ping db
docker exec -it api curl http://api:3000/health
# Run a temporary debug container on the network
docker run --rm -it --network app-network nicolaka/netshoot
# Inside: dig redis, nslookup db, curl api:3000, tcpdump, etc.
# Check published ports
docker port my-container
# View iptables rules Docker created (Linux)
sudo iptables -t nat -L -n | grep DOCKERReferences
- Docker docs — Networking overview
- Docker docs — Bridge driver
- Docker docs — Overlay driver
- Docker docs — Compose networking
- nicolaka/netshoot — debug toolkit container
- Linux man pages —
iptables(8)
Key Takeaways
- • Always use user-defined bridge networks — they provide automatic DNS resolution between containers
- • The default
docker0bridge lacks DNS and should be avoided - • Port mapping (
-p host:container) is required to expose container ports to the host - • Docker Compose creates a project network automatically — services resolve by service name
- • Use
internal: truenetworks to isolate backend services from external access - • Overlay networks enable cross-host communication in Swarm deployments
- • Use
docker network inspectand netshoot for debugging connectivity
Generate a production-ready Dockerfile for any stack with the Dockerfile Generator tool.