Skip to content

Docker Networking Deep Dive — Networks, DNS, and Traffic Control

DodaTech Updated 2026-06-24 8 min read

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 is the difference between docker network and docker compose network?

Docker Compose creates a default network per project. Services communicate by service name. It's equivalent to a user-defined bridge but with automatic lifecycle management — Compose creates the network on up and removes it on down.

Can containers on different hosts communicate without overlay?

Yes. Use macvlan (containers appear on the physical network) or expose ports and connect via the host's IP. Overlay is preferred for multi-host because it handles encryption, service discovery, and Load Balancing automatically.

Why can't I ping google.com from inside a container?

The container needs DNS configuration and internet routing. Ensure /etc/resolv.conf has valid nameservers. Check that the host has ip_forward enabled: sysctl net.ipv4.ip_forward=1.

What's Next

Docker Restart Policies & Health Checks
Docker Compose Networks
What is Docker?

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