Docker Networking Deep Dive — Networks, DNS, and Traffic Control
In this tutorial, you'll learn about Docker Networking Deep Dive. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
Docker networking connects containers to each other, to the host, and to external networks through pluggable network drivers, each providing different isolation, performance, and reachability characteristics.
What You'll Learn
You'll master Docker's networking stack — bridge, host, overlay, and macvlan drivers, DNS-based service discovery, port publishing mechanics, and network security best practices.
Why This Problem Matters
Understanding Docker networking is essential for designing multi-container applications. A misconfigured network causes connectivity issues, security holes, or performance bottlenecks. Docker's five built-in network drivers each serve different use cases.
Real-World Use
Doda Browser's malware analysis pipeline uses Docker overlay networks to connect analysis containers across multiple hosts, ensuring that isolated analysis environments cannot communicate with each other while sharing a management network.
Network Driver Architecture
flowchart TB
subgraph Host
D[Docker Daemon]
subgraph BridgeNet
C1[Container 1
172.17.0.2]
C2[Container 2
172.17.0.3]
end
subgraph HostNet
C3[Container 3
Host IP]
end
subgraph MacvlanNet
C4[Container 4
192.168.1.100]
end
D --- BridgeNet
D --- HostNet
D --- MacvlanNet
end
subgraph External
WWW[Internet]
DB[Database Server]
end
BridgeNet -->|NAT| WWW
MacvlanNet ---|Direct L2| DB
Bridge Network (Default)
Containers on the same bridge can communicate. Containers on different bridges are isolated:
# List networks
docker network ls
# Create a custom bridge network
docker network create --driver bridge \
--subnet 172.28.0.0/16 \
--gateway 172.28.0.1 \
my-app-net
# Run containers on the custom network
docker run -d --name web --network my-app-net nginx
docker run -d --name db --network my-app-net postgres:16
# Test connectivity
docker exec web ping db
Expected output:
PING db (172.28.0.3) 56(84) bytes of data.
64 bytes from db.my-app-net (172.28.0.3): icmp_seq=1 ttl=64 time=0.083ms
Port Publishing
# Publish container port to host
docker run -d --name web -p 8080:80 nginx
# Publish on specific interface
docker run -d --name api -p 127.0.0.1:3000:3000 myapi
# Publish UDP port
docker run -d --name dns -p 53:53/udp coredns
# Bind every port exposed in Dockerfile
docker run -d --name web -P nginx
import socket
def check_port(host: str, port: int) -> bool:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1)
result = sock.connect_ex((host, port))
sock.close()
return result == 0
# Check if port 8080 is accepting connections
print(f"Port 8080 open: {check_port('localhost', 8080)}")
print(f"Port 3000 open: {check_port('localhost', 3000)}")
Expected output:
Port 8080 open: True
Port 3000 open: True
DNS-Based Service Discovery
Docker's embedded DNS server resolves container names to IPs within the same network:
import docker
client = docker.from_env()
# Create network
net = client.networks.create("discovery-demo", driver="bridge")
# Start a Redis container
redis = client.containers.run(
"redis:7-alpine",
name="redis-cache",
network="discovery-demo",
detach=True
)
# Start an app container that connects to Redis by name
app = client.containers.run(
"alpine",
"sh -c 'ping -c 2 redis-cache'",
network="discovery-demo",
detach=True
)
for line in app.logs(stream=True):
print(line.decode().strip())
redis.remove(force=True)
app.remove(force=True)
net.remove()
Expected output:
PING redis-cache (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.098 ms
Host Network Driver
The container shares the host's network stack — no isolation, no NAT:
docker run -d --name nginx-host --network host nginx
# The container is directly on the host's IP
curl http://localhost:80
# Compare: bridge mode adds NAT overhead
docker run -d --name nginx-bridge -p 8080:80 nginx
Expected output:
Welcome to nginx!
Host mode is ideal for latency-sensitive applications (VoIP, gaming servers) where NAT overhead must be avoided.
Overlay Network (Multi-Host)
Overlay networks connect containers across multiple Docker hosts using VXLAN Encapsulation:
# Initialize Swarm (required for overlay)
docker swarm init
# Create overlay network
docker network create --driver overlay \
--subnet 10.0.0.0/16 \
--attachable \
my-overlay
# Run services across nodes
docker service create --name web \
--network my-overlay \
--replicas 3 \
nginx
Macvlan Network
Assigns a MAC address to each container, making them appear as physical devices on the network:
# Create macvlan network (requires specific host interface)
docker network create --driver macvlan \
--subnet 192.168.1.0/24 \
--gateway 192.168.1.1 \
--ip-range 192.168.1.64/28 \
--aux-address "reserved=192.168.1.64" \
-o parent=eth0 \
my-macvlan
Network Traffic Simulation
import socket
import threading
import time
class ContainerSimulator:
def __init__(self, name: str, ip: str, subnet: str):
self.name = name
self.ip = ip
self.subnet = subnet
self.peers = {}
def add_peer(self, other):
self.peers[other.name] = other
def ping(self, target_name: str) -> float:
if target_name in self.peers:
target = self.peers[target_name]
if self.subnet == target.subnet:
latency = 0.1 # Same network: 0.1ms
else:
latency = 1.5 # Different network: 1.5ms via gateway
return latency
return None
def http_get(self, target_name: str) -> str:
latency = self.ping(target_name)
if latency is not None:
data_size = random.randint(100, 5000)
return f"200 OK ({data_size} bytes in {latency + 0.5:.1f}ms)"
return "Destination unreachable"
import random
web = ContainerSimulator("web", "172.17.0.2", "172.17.0.0/16")
db = ContainerSimulator("db", "172.17.0.3", "172.17.0.0/16")
cache = ContainerSimulator("redis", "172.18.0.2", "172.18.0.0/16")
web.add_peer(db)
web.add_peer(cache)
db.add_peer(web)
print(f"web -> db: {web.http_get('db')}")
print(f"web -> redis: {web.http_get('redis')}")
Expected output:
web -> db: 200 OK (2340 bytes in 0.6ms)
web -> redis: Destination unreachable
Common Mistakes
1. Using Default Bridge for Multi-Container Apps
The default bridge lacks DNS resolution (containers must use IPs). Always create a custom user-defined bridge for multi-container apps so they can resolve each other by name.
2. Exposing All Ports to the World
-p 3000:3000 binds to 0.0.0.0 by default, exposing the port externally. Use -p 127.0.0.1:3000:3000 for local-only access or restrict with firewall rules.
3. Mixing networks Without Route Configuration
A container on two networks cannot route traffic between them without explicit routing. Use --network-alias or a reverse proxy to bridge networks.
4. Overlay Network Performance Assumptions
Overlay networks add VXLAN Encapsulation overhead (approx 5-10% bandwidth loss). For high-throughput scenarios, consider macvlan or host networking.
5. IP Address Conflicts with Macvlan
Macvlan assigns IPs from your physical network. DHCP conflicts or IP exhaustion can occur if not coordinated with your network administrator.
6. Forgetting iptables Rules
Docker manipulates iptables to implement network isolation. If you have custom iptables rules, they may conflict with Docker's rules.
7. No Network Security Policies
On a shared bridge network, any container can reach any other container. Use separate networks for different trust levels (public-facing, backend, database).
Practice Questions
1. What is the default network driver and when should you avoid it?
The default is bridge. Avoid it when you need DNS resolution between containers — use a user-defined bridge instead. The default bridge does not support container name resolution.
2. How does Docker DNS work?
Docker runs an embedded DNS server at 127.0.0.11 inside each container. Container names are resolved to IPs within the same network. Cross-network lookups fail unless the container is attached to both networks.
3. What is the difference between macvlan and ipvlan?
Macvlan assigns a unique MAC address to each container (consuming MACs from the physical NIC). Macvlan cannot communicate with the host directly. Ipvlan shares the host MAC and allows host-container communication.
4. How do you connect a container to multiple networks?
docker network connect <network> <container> attaches an additional network interface. The container gets an IP on each network. Use this for patterns like "web on frontend and backend networks."
5. Challenge: Design a multi-tenant network isolation scheme.
Tenant A's containers and Tenant B's containers run on the same Docker host. Each tenant must be fully isolated — no network communication between tenants. Both tenants must reach the internet. Design the network topology.
Mini Project: Network Topology Simulator
class DockerNetwork:
def __init__(self, name: str, driver: str, subnet: str):
self.name = name
self.driver = driver
self.subnet = subnet
self.containers = {}
def attach(self, container: str, ip: str = None):
self.containers[container] = ip or f"{self.subnet.rstrip('0')}{len(self.containers) + 2}"
def route(self, target_ip: str) -> str:
for c, ip in self.containers.items():
if ip == target_ip:
return f"Reached {c} via {self.name}"
return None
class DockerHost:
def __init__(self):
self.networks = {}
def add_network(self, net: DockerNetwork):
self.networks[net.name] = net
def connect(self, container: str, network: str, ip: str = None):
self.networks[network].attach(container, ip)
def communicate(self, source: str, target_ip: str) -> str:
for net in self.networks.values():
if source in net.containers:
result = net.route(target_ip)
if result:
return result
return f"Target {target_ip} unreachable from {source}"
host = DockerHost()
frontend = DockerNetwork("frontend", "bridge", "172.17.0.0/16")
backend = DockerNetwork("backend", "bridge", "172.18.0.0/16")
host.add_network(frontend)
host.add_network(backend)
host.connect("nginx", "frontend", "172.17.0.2")
host.connect("app", "frontend", "172.17.0.3")
host.connect("app", "backend", "172.18.0.2")
host.connect("postgres", "backend", "172.18.0.3")
print(host.communicate("nginx", "172.17.0.3"))
print(host.communicate("nginx", "172.18.0.3"))
Expected output:
Reached app via frontend
Target 172.18.0.3 unreachable from nginx
FAQ
What's Next
Congratulations on completing this networking deep dive! Here's where to go from here:
- Practice daily — Experiment with each network driver using
docker network - Build a project — Set up a multi-host overlay network with Docker Swarm
- Explore related topics — VXLAN Encapsulation, iptables rules, network namespaces
- Join the community — Share your network topologies and get feedback
Remember: every expert was once a beginner. Keep connecting!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro