Docker Networking Deep Dive: Bridge, Host, Overlay, and Macvlan
Master Docker networking. Bridge networks, DNS resolution, port publishing, overlay networks for Swarm, macvlan for direct LAN access, and troubleshooting...
Docker Networking Fundamentals
Docker networking is one of the most misunderstood aspects of containers. Understanding how containers communicate is essential for building reliable, secure infrastructure.
<div style="margin:2.5rem auto;max-width:600px;width:100%;text-align:center;"><svg viewBox="0 0 600 200" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;"><rect width="600" height="200" rx="12" fill="#1a1a2e"/><rect x="30" y="30" width="100" height="130" rx="6" fill="none" stroke="#3b82f6" stroke-width="1.5"/><text x="80" y="55" text-anchor="middle" fill="#3b82f6" font-size="10" font-family="monospace">docker-</text><text x="80" y="70" text-anchor="middle" fill="#3b82f6" font-size="10" font-family="monospace">compose</text><text x="80" y="85" text-anchor="middle" fill="#3b82f6" font-size="10" font-family="monospace">.yml</text><line x1="45" y1="95" x2="115" y2="95" stroke="#3b82f6" stroke-width="0.5" opacity="0.5"/><rect x="50" y="105" width="50" height="8" rx="2" fill="#94a3b8" opacity="0.3"/><rect x="50" y="118" width="60" height="8" rx="2" fill="#94a3b8" opacity="0.3"/><rect x="50" y="131" width="40" height="8" rx="2" fill="#94a3b8" opacity="0.3"/><path d="M135,95 L175,95" stroke="#e2e8f0" stroke-width="2" marker-end="url(#arrow2)"/><defs><marker id="arrow2" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6" fill="#e2e8f0"/></marker></defs><rect x="180" y="20" width="130" height="35" rx="6" fill="#6366f1" opacity="0.85"/><text x="245" y="42" text-anchor="middle" fill="#ffffff" font-size="11" font-family="system-ui">Web App</text><rect x="180" y="62" width="130" height="35" rx="6" fill="#a855f7" opacity="0.85"/><text x="245" y="84" text-anchor="middle" fill="#ffffff" font-size="11" font-family="system-ui">API Server</text><rect x="180" y="104" width="130" height="35" rx="6" fill="#2dd4bf" opacity="0.85"/><text x="245" y="126" text-anchor="middle" fill="#1a1a2e" font-size="11" font-family="system-ui">Database</text><rect x="180" y="146" width="130" height="35" rx="6" fill="#f59e0b" opacity="0.85"/><text x="245" y="168" text-anchor="middle" fill="#1a1a2e" font-size="11" font-family="system-ui">Cache</text><rect x="370" y="40" width="200" height="130" rx="8" fill="none" stroke="#e2e8f0" stroke-width="1" stroke-dasharray="5,4"/><text x="470" y="62" text-anchor="middle" fill="#e2e8f0" font-size="10" font-family="system-ui">Docker Network</text><line x1="310" y1="37" x2="390" y2="80" stroke="#94a3b8" stroke-width="1" opacity="0.5"/><line x1="310" y1="79" x2="390" y2="100" stroke="#94a3b8" stroke-width="1" opacity="0.5"/><line x1="310" y1="121" x2="390" y2="120" stroke="#94a3b8" stroke-width="1" opacity="0.5"/><line x1="310" y1="163" x2="390" y2="140" stroke="#94a3b8" stroke-width="1" opacity="0.5"/><circle cx="400" cy="80" r="5" fill="#6366f1"/><circle cx="400" cy="100" r="5" fill="#a855f7"/><circle cx="400" cy="120" r="5" fill="#2dd4bf"/><circle cx="400" cy="140" r="5" fill="#f59e0b"/><text x="470" y="85" text-anchor="middle" fill="#94a3b8" font-size="10" font-family="system-ui">:3000</text><text x="470" y="105" text-anchor="middle" fill="#94a3b8" font-size="10" font-family="system-ui">:8080</text><text x="470" y="125" text-anchor="middle" fill="#94a3b8" font-size="10" font-family="system-ui">:5432</text><text x="470" y="145" text-anchor="middle" fill="#94a3b8" font-size="10" font-family="system-ui">:6379</text></svg><p style="margin-top:0.75rem;font-size:0.85rem;color:#94a3b8;font-style:italic;line-height:1.4;">Docker Compose defines your entire application stack in a single YAML file.</p></div>
Network Drivers
Docker provides four main network drivers:
1. Bridge Network (Default)
Bridge is the default network driver. Containers on the same bridge network can communicate by container name (DNS resolution). Containers on different bridge networks are isolated.
# Create a custom bridge network
docker network create --driver bridge app-net
# Run containers on the same network
docker run -d --name api --network app-net my-api:latest
docker run -d --name db --network app-net postgres:16
# The api container can reach the db container by name:
# postgresql://db:5432/myappDocker Compose automatically creates a bridge network:
# docker-compose.yml
services:
api:
image: my-api:latest
networks:
- app-net
db:
image: postgres:16
networks:
- app-net
redis:
image: redis:7
networks:
- app-net
networks:
app-net:
driver: bridgeAll three containers can reach each other by service name: api, db, redis.
How bridge networking works under the hood:
Host Network Namespace
├── eth0 (192.168.1.100) ← Physical interface
├── docker0 (172.17.0.1) ← Default bridge
├── br-abc123 (172.18.0.1) ← Custom bridge (app-net)
│ ├── vethXXX → api (172.18.0.2)
│ ├── vethYYY → db (172.18.0.3)
│ └── vethZZZ → redis (172.18.0.4)
└── iptables NAT rules ← Port publishingEach container gets a virtual ethernet interface (veth pair) connected to the bridge. Docker runs an embedded DNS server that resolves container names to their IPs.
2. Host Network
Host networking removes network isolation. The container shares the host's network stack directly.
docker run -d --name nginx --network host nginx:alpine
# Nginx listens on port 80 of the host directly
# No port mapping needed (or possible)# docker-compose.yml
services:
cloudflared:
image: cloudflare/cloudflared:latest
network_mode: host
# No ports: section needed - it binds to host directlyWhen to use host networking:
Downsides: No port isolation, containers can conflict, cannot run multiple instances of the same service.
3. Overlay Network (Docker Swarm)
Overlay networks span multiple Docker hosts, allowing containers on different machines to communicate as if on the same LAN.
# Initialize Swarm
docker swarm init
# Create overlay network
docker network create --driver overlay --attachable app-overlay
# Services on different nodes can communicate
docker service create --name api --network app-overlay my-api:latest
docker service create --name db --network app-overlay postgres:16<div style="margin:2.5rem auto;max-width:600px;width:100%;text-align:center;"><svg viewBox="0 0 600 190" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;"><rect width="600" height="190" rx="12" fill="#0d1117"/><rect x="0" y="0" width="600" height="28" rx="12" fill="#1c2333"/><rect x="0" y="12" width="600" height="16" fill="#1c2333"/><circle cx="18" cy="14" r="5" fill="#ef4444"/><circle cx="34" cy="14" r="5" fill="#f59e0b"/><circle cx="50" cy="14" r="5" fill="#2dd4bf"/><text x="300" y="18" text-anchor="middle" fill="#94a3b8" font-size="10" font-family="monospace">Terminal</text><text x="20" y="50" fill="#2dd4bf" font-size="11" font-family="monospace">$</text><text x="35" y="50" fill="#e2e8f0" font-size="11" font-family="monospace">docker compose up -d</text><text x="20" y="70" fill="#94a3b8" font-size="11" font-family="monospace">[+] Running 5/5</text><text x="20" y="88" fill="#2dd4bf" font-size="10" font-family="monospace"> ✓</text><text x="38" y="88" fill="#94a3b8" font-size="10" font-family="monospace">Network app_default Created</text><text x="20" y="106" fill="#2dd4bf" font-size="10" font-family="monospace"> ✓</text><text x="38" y="106" fill="#94a3b8" font-size="10" font-family="monospace">Container web Started</text><text x="20" y="124" fill="#2dd4bf" font-size="10" font-family="monospace"> ✓</text><text x="38" y="124" fill="#94a3b8" font-size="10" font-family="monospace">Container api Started</text><text x="20" y="142" fill="#2dd4bf" font-size="10" font-family="monospace"> ✓</text><text x="38" y="142" fill="#94a3b8" font-size="10" font-family="monospace">Container db Started</text><text x="20" y="165" fill="#2dd4bf" font-size="11" font-family="monospace">$</text><rect x="35" y="155" width="8" height="14" fill="#e2e8f0" opacity="0.7"/></svg><p style="margin-top:0.75rem;font-size:0.85rem;color:#94a3b8;font-style:italic;line-height:1.4;">Docker Compose brings up your entire stack with a single command.</p></div>
4. Macvlan Network
Macvlan gives each container its own MAC address on the physical network. Containers appear as physical devices on the LAN.
# Create macvlan network
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 \
lan-net
# Container gets a real LAN IP
docker run -d --name pihole \
--network lan-net \
--ip 192.168.1.50 \
pihole/pihole:latestWhen to use macvlan: IoT devices, network appliances (Pi-hole, DHCP server), or when containers need to be directly accessible on the LAN.
DNS Resolution in Docker
Docker's embedded DNS server (127.0.0.11) handles service discovery:
# From inside a container
nslookup db
# Server: 127.0.0.11
# Name: db
# Address: 172.18.0.3
# Docker Compose also creates aliases
nslookup postgres
# Resolves to the 'db' container if that's the service nameDNS aliases with network aliases:
services:
database:
image: postgres:16
networks:
app-net:
aliases:
- db
- postgres
- pg
# Container reachable as: database, db, postgres, pgPort Publishing
Port publishing maps host ports to container ports via iptables NAT:
services:
api:
image: my-api:latest
ports:
# host:container
- "3000:3000" # All interfaces
- "127.0.0.1:3001:3001" # Localhost only
- "8080:80/tcp" # Explicit TCP
- "8443:443/udp" # UDPSecurity tip: Bind to 127.0.0.1 for services that should only be accessible locally or through a reverse proxy:
services:
# Only Traefik is exposed
traefik:
ports:
- "80:80"
- "443:443"
# Internal services - no ports published
api:
# No ports: section. Only reachable via Docker network (through Traefik)
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`api.example.com`)"Multi-Network Architecture
Isolate services by putting them on different networks:
services:
traefik:
networks:
- frontend
- backend # Can reach both
api:
networks:
- backend
- database # Can reach db, not frontend directly
db:
networks:
- database # Only reachable from database network
redis:
networks:
- database
networks:
frontend: # External-facing services
backend: # Application services
database: # Data layer (most isolated)Troubleshooting Docker Networking
# Inspect network details
docker network inspect app-net
# List all networks
docker network ls
# Check container's network settings
docker inspect --format='{{json .NetworkSettings.Networks}}' container-name
# Test connectivity from inside a container
docker exec -it api ping db
docker exec -it api nslookup db
docker exec -it api wget -qO- http://db:5432
# Debug with a network tools container
docker run -it --rm --network app-net nicolaka/netshoot
# Inside netshoot:
nslookup api
curl http://api:3000/health
tcpdump -i eth0 port 5432
iperf3 -c db -p 5201
# Check iptables rules (port publishing)
sudo iptables -t nat -L DOCKER -n
# Check if port is published correctly
ss -tlnp | grep 3000<div style="margin:2.5rem auto;max-width:600px;width:100%;text-align:center;"><svg viewBox="0 0 600 220" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;"><rect width="600" height="220" rx="12" fill="#1a1a2e"/><rect x="200" y="15" width="200" height="40" rx="8" fill="#6366f1"/><text x="300" y="40" text-anchor="middle" fill="#ffffff" font-size="13" font-family="system-ui" font-weight="bold">Orchestrator</text><line x1="250" y1="55" x2="100" y2="90" stroke="#e2e8f0" stroke-width="1.5" stroke-dasharray="4,3"/><line x1="300" y1="55" x2="300" y2="90" stroke="#e2e8f0" stroke-width="1.5" stroke-dasharray="4,3"/><line x1="350" y1="55" x2="500" y2="90" stroke="#e2e8f0" stroke-width="1.5" stroke-dasharray="4,3"/><rect x="40" y="90" width="120" height="110" rx="8" fill="none" stroke="#3b82f6" stroke-width="1.5"/><text x="100" y="110" text-anchor="middle" fill="#3b82f6" font-size="11" font-family="system-ui">Node 1</text><rect x="55" y="120" width="90" height="25" rx="4" fill="#6366f1" opacity="0.7"/><text x="100" y="137" text-anchor="middle" fill="#ffffff" font-size="10" font-family="system-ui">Container A</text><rect x="55" y="150" width="90" height="25" rx="4" fill="#a855f7" opacity="0.7"/><text x="100" y="167" text-anchor="middle" fill="#ffffff" font-size="10" font-family="system-ui">Container B</text><rect x="240" y="90" width="120" height="110" rx="8" fill="none" stroke="#3b82f6" stroke-width="1.5"/><text x="300" y="110" text-anchor="middle" fill="#3b82f6" font-size="11" font-family="system-ui">Node 2</text><rect x="255" y="120" width="90" height="25" rx="4" fill="#2dd4bf" opacity="0.7"/><text x="300" y="137" text-anchor="middle" fill="#1a1a2e" font-size="10" font-family="system-ui">Container C</text><rect x="255" y="150" width="90" height="25" rx="4" fill="#6366f1" opacity="0.7"/><text x="300" y="167" text-anchor="middle" fill="#ffffff" font-size="10" font-family="system-ui">Container A</text><rect x="440" y="90" width="120" height="110" rx="8" fill="none" stroke="#3b82f6" stroke-width="1.5"/><text x="500" y="110" text-anchor="middle" fill="#3b82f6" font-size="11" font-family="system-ui">Node 3</text><rect x="455" y="120" width="90" height="25" rx="4" fill="#a855f7" opacity="0.7"/><text x="500" y="137" text-anchor="middle" fill="#ffffff" font-size="10" font-family="system-ui">Container B</text><rect x="455" y="150" width="90" height="25" rx="4" fill="#f59e0b" opacity="0.7"/><text x="500" y="167" text-anchor="middle" fill="#1a1a2e" font-size="10" font-family="system-ui">Container D</text></svg><p style="margin-top:0.75rem;font-size:0.85rem;color:#94a3b8;font-style:italic;line-height:1.4;">Container orchestration distributes workloads across multiple nodes for resilience and scale.</p></div>
Common Networking Mistakes
1. Using default bridge: The default bridge does not support DNS resolution between containers. Always create a custom bridge network.
2. Publishing unnecessary ports: Only publish ports that need external access. Internal services should communicate via Docker networks.
3. Hardcoding IPs: Container IPs change on restart. Always use container names (DNS).
4. Ignoring network isolation: Put databases on a separate network from external-facing services.
5. Forgetting DNS cache: Some containers cache DNS. If a container IP changes, others may have stale entries. Use the embedded DNS resolver.
At TechSaaS, we use a single custom bridge network (padc-net) for all our Docker containers. Traefik is the only container with published ports (80). All 50+ services communicate via container names over the bridge network, and databases are accessible only from within the Docker network. This is the simplest and most secure networking setup for a single-host deployment.
Need help with tutorials?
TechSaaS provides expert consulting and managed services for cloud infrastructure, DevOps, and AI/ML operations.