← Back to DevBytes

Docker Networks: Bridge, Host, Overlay, Macvlan: Best Practices and Common Pitfalls

Docker Networking Overview

Docker networking is the subsystem that governs how containers communicate with each other, with the host system, and with the outside world. When you run a container, Docker creates or attaches network interfaces, assigns IP addresses, and configures routing rules automatically. However, the default behavior is rarely sufficient for production workloads. Understanding the four primary network drivers — bridge, host, overlay, and macvlan — lets you design secure, performant, and scalable container architectures while avoiding the silent failures and security gaps that plague poorly networked deployments.

Each driver solves a distinct set of problems. Choosing the wrong one can expose services unintentionally, break service discovery, or cause intermittent connectivity failures that are notoriously difficult to debug. This tutorial walks you through every driver in depth, with practical code examples, configuration details, and hard-won best practices drawn from real-world production experience.

Bridge Networks

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

What It Is

The bridge driver is Docker's default networking mode. It creates a virtual software bridge on the host (typically docker0) that acts like a physical Ethernet switch, forwarding traffic between containers connected to it. Each container gets a private IP address from a subnet allocated to that bridge, and containers on the same bridge can communicate directly using IP addresses or container names via Docker's built-in DNS resolver. Communication with the outside world happens through Network Address Translation (NAT) on the host's external interface.

Default Bridge vs User-Defined Bridge

Docker ships with a default bridge named bridge. Containers attached to it can communicate only by IP address — automatic DNS resolution does not work on the default bridge. This is a deliberate design choice to preserve backward compatibility but causes endless confusion for newcomers. A user-defined bridge (created with docker network create) provides automatic DNS resolution, better isolation, and the ability to attach and detach containers at runtime without restarting them.

Why It Matters

How to Use It

Create a user-defined bridge network:

# Create a bridge network with a custom subnet and IP range
docker network create \
  --driver bridge \
  --subnet 10.42.0.0/16 \
  --ip-range 10.42.5.0/24 \
  --gateway 10.42.0.1 \
  my-bridge

# Inspect the network to verify configuration
docker network inspect my-bridge

Run containers attached to the bridge:

# Start a web server on the custom bridge
docker run -d \
  --name web-app \
  --network my-bridge \
  --ip 10.42.5.10 \
  nginx:alpine

# Start a database on the same bridge
docker run -d \
  --name postgres-db \
  --network my-bridge \
  -e POSTGRES_PASSWORD=secret \
  postgres:15-alpine

# The web container can resolve 'postgres-db' via Docker DNS
docker exec web-app ping postgres-db

Publish a port to the host so external clients can reach the container:

# Map host port 8080 to container port 80
docker run -d \
  --name web-app \
  --network my-bridge \
  -p 8080:80 \
  nginx:alpine

# Verify the port mapping
docker port web-app
# Output: 80/tcp -> 0.0.0.0:8080

Connect a running container to an additional bridge network:

# Create a second bridge network for monitoring tools
docker network create monitoring-net

# Connect the running web-app container to it dynamically
docker network connect monitoring-net web-app

# Verify connections
docker inspect web-app --format='{{json .NetworkSettings.Networks}}' | jq 'keys'

Best Practices for Bridge Networks

Common Pitfalls

Host Network

What It Is

The host driver removes Docker's network virtualization entirely. The container shares the host's network namespace directly — it sees and uses all host network interfaces, listens on the host's IP addresses, and binds to ports as if it were a process running natively on the host. There is no separate IP address, no bridge, no NAT layer. Running with --network host gives the container unfiltered access to the host's entire networking stack.

Why It Matters

How to Use It

Run a container in host network mode:

# Run nginx directly on the host's network stack
docker run -d \
  --name host-nginx \
  --network host \
  nginx:alpine

# The web server is now accessible on host-ip:80
# No docker port mapping required
curl http://localhost:80

# Verify the container sees host interfaces
docker exec host-nginx ip addr show
# You will see eth0, lo, wlan0 etc. — the host's actual interfaces

Compare network visibility between host and bridge modes:

# Bridge mode: isolated namespace
docker run --rm --network bridge alpine:latest ip addr show
# Output: eth0@if123 with 172.17.0.x address, lo with 127.0.0.1

# Host mode: full host visibility
docker run --rm --network host alpine:latest ip addr show
# Output: all host interfaces exactly as the host sees them

A typical use case — running a network debugging container with host privileges:

# Run tcpdump with full host network access
docker run --rm --network host \
  --cap-add NET_RAW \
  --cap-add NET_ADMIN \
  alpine:latest \
  tcpdump -i eth0 -n port 443

Best Practices for Host Networking

Common Pitfalls

Overlay Networks

What It Is

The overlay driver enables containers running on different Docker hosts to communicate directly as if they were on the same physical network. It creates a distributed virtual network that spans multiple Docker daemons, encapsulating container traffic through VXLAN tunnels (or encrypted with IPsec when using --opt encrypted). Overlay networks are fundamental to Docker Swarm mode, where services spread across worker nodes need seamless cross-host networking with built-in service discovery and load balancing.

Why It Matters

How to Use It

Initialize a Swarm cluster and create an overlay network:

# On the manager node, initialize Swarm
docker swarm init --advertise-addr 192.168.1.100

# On worker nodes, join the swarm
docker swarm join --token SWMTKN-1-xxx 192.168.1.100:2377

# Create an encrypted overlay network (only on manager)
docker network create \
  --driver overlay \
  --opt encrypted \
  --subnet 10.100.0.0/24 \
  --gateway 10.100.0.1 \
  --attachable \
  my-overlay

# Verify the network spans all Swarm nodes
docker network ls
docker network inspect my-overlay

Deploy services across the overlay:

# Create a backend service with 3 replicas on the overlay
docker service create \
  --name api \
  --network my-overlay \
  --replicas 3 \
  --publish 8080:8080 \
  my-api-image:latest

# Create a database service (no external port, overlay-only)
docker service create \
  --name db \
  --network my-overlay \
  --replicas 1 \
  -e POSTGRES_PASSWORD=secret \
  postgres:15-alpine

# The API service can reach the DB via DNS name 'db'
# Docker's internal load balancer distributes traffic across API replicas

Attach standalone (non-Swarm) containers to an overlay:

# With --attachable flag set during network creation
docker run -d \
  --name debug-tool \
  --network my-overlay \
  alpine:latest \
  sleep infinity

# Now debug-tool can ping 'api' and 'db' service names
docker exec debug-tool ping api
docker exec debug-tool nslookup db

Best Practices for Overlay Networks

Common Pitfalls

Macvlan Networks

What It Is

The macvlan driver assigns a unique MAC address to each container, making it appear as a distinct physical device on the network. Unlike bridge and overlay modes, containers on a macvlan network bypass the host's network stack and connect directly to the physical network interface (or a sub-interface for VLAN trunking). External DHCP servers, routers, and firewalls see each container as an independent network node with its own MAC and IP address.

Why It Matters

How to Use It

Create a macvlan network in bridge mode (containers can communicate through the external switch):

# Create a macvlan network on eth0 with a specified subnet and gateway
docker network create \
  --driver macvlan \
  --subnet 192.168.1.0/24 \
  --gateway 192.168.1.1 \
  --ip-range 192.168.1.200/29 \
  --aux-address 'router=192.168.1.254' \
  -o parent=eth0 \
  macvlan-net

# Run a container that appears as a separate physical device
docker run -d \
  --name macvlan-app \
  --network macvlan-net \
  --ip 192.168.1.201 \
  nginx:alpine

# The container is now reachable at 192.168.1.201 from any device on the LAN
# No port mapping needed — it's a direct network citizen

Create a VLAN-tagged macvlan for network segmentation:

# First, create a VLAN sub-interface on the host (Linux)
ip link add link eth0 name eth0.100 type vlan id 100
ip addr add 10.100.0.1/24 dev eth0.100
ip link set eth0.100 up

# Create a macvlan network using the VLAN sub-interface
docker network create \
  --driver macvlan \
  --subnet 10.100.0.0/24 \
  --gateway 10.100.0.1 \
  -o parent=eth0.100 \
  vlan100-net

# Containers on this network are in VLAN 100
docker run -d \
  --name vlan-app \
  --network vlan100-net \
  --ip 10.100.0.50 \
  nginx:alpine

Use macvlan in 802.1Q mode (Docker manages VLAN tagging internally):

# Create a macvlan network with built-in VLAN trunking
# Requires the parent interface to be in trunk mode
docker network create \
  --driver macvlan \
  --subnet 10.200.0.0/24 \
  --gateway 10.200.0.1 \
  -o parent=eth0 \
  -o macvlan_mode=bridge \
  vlan200-net

# Docker handles the VLAN ID internally
docker run -d \
  --name trunk-app \
  --network vlan200-net \
  nginx:alpine

Best Practices for Macvlan Networks

Common Pitfalls

Cross-Cutting Best Practices

IP Address Management

Plan your subnet allocations across all network drivers holistically. Maintain a network topology document that tracks bridge subnets, overlay subnets, macvlan ranges, and any external network segments containers may interact with. Overlapping subnets cause routing black holes that are difficult to diagnose because traffic may be silently dropped rather than generating clear error messages.

# Example: auditing all Docker networks on a host
docker network ls -q | xargs docker network inspect \
  --format='{{.Name}}: {{range .IPAM.Config}}{{.Subnet}}{{end}}'

Network Security Tiering

Treat Docker networks as security boundaries. Place public-facing services on one bridge or overlay network, internal application services on another, and databases on a third. Use Docker's network-level isolation to enforce that only authorized services can reach sensitive components, supplementing application-level authentication.

# Create tiered networks for a three-tier application
docker network create --driver bridge --subnet 10.10.1.0/24 frontend-net
docker network create --driver bridge --subnet 10.10.2.0/24 app-net
docker network create --driver bridge --subnet 10.10.3.0/24 data-net

# Frontend connects to frontend-net only
# Application service connects to both frontend-net and app-net
# Database connects to app-net and data-net (or just data-net)
# This enforces that frontend cannot directly reach the database

DNS and Service Discovery

Docker's embedded DNS server at 127.0.0.11 resolves container names to IP addresses on user-defined bridge and overlay networks. Understand its behavior: it returns the container's IP (not a virtual IP) for single containers and a virtual IP for Swarm services. For production, always test DNS resolution behavior under failure conditions — when containers restart, IP addresses change, and DNS TTLs determine how quickly peers notice the update.

# Inspect DNS configuration inside a container
docker exec web-app cat /etc/resolv.conf
# nameserver 127.0.0.11
# options ndots:0

# Test resolution from within a container
docker exec web-app nslookup postgres-db

Performance Considerations

Each network driver imposes different overhead profiles. Bridge mode adds veth pair and NAT processing. Overlay adds VXLAN encapsulation and decapsulation. Macvlan adds minimal overhead but sacrifices isolation. Host mode eliminates all overhead but sacrifices portability and isolation. Benchmark your specific workload — don't rely on generic guidance — and measure latency, throughput, and CPU utilization under realistic load.

Cleanup and Maintenance

Unused networks accumulate, especially in development environments. Regularly prune networks to prevent subnet exhaustion and configuration drift:

# Remove all unused networks
docker network prune

# Force removal of all unused networks without confirmation prompt
docker network prune -f

# List networks and their container counts to identify orphans
docker network ls --format='{{.Name}}: {{.Containers}}' | grep ': 0'

Conclusion

Docker's four primary network drivers form a toolkit that spans nearly every container networking requirement. Bridge networks provide the best balance of isolation and usability for single-host workloads. Host networking delivers raw performance for specialized applications at the cost of isolation. Overlay networks unlock multi-host service meshes essential for Swarm-based distributed systems. Macvlan bridges containers directly into physical networks for legacy compatibility and line-rate throughput.

The key to mastering Docker networking is not memorizing flags but understanding the fundamental trade-offs each driver makes between isolation, performance, complexity, and compatibility. Start with user-defined bridge networks for development and single-host production. Graduate to overlay networks when you need horizontal scaling across hosts. Reserve host and macvlan modes for the narrow use cases where their specific advantages outweigh their significant constraints. Most importantly, treat network configuration as first-class infrastructure code — document subnets, enforce tiered security boundaries, and test connectivity behavior under failure conditions before relying on any network topology in production.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles