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
- Isolation: Each user-defined bridge is a separate broadcast domain. Containers on different bridge networks cannot communicate unless you explicitly connect them or route between networks.
- Service discovery: DNS resolution on user-defined bridges lets containers find each other by name, enabling zero-config service discovery for microservices.
- Security: The default bridge exposes all ports to all other containers on that bridge. User-defined bridges let you control which containers share a network.
- Dynamic attach/detach: You can connect a running container to a user-defined bridge without restarting it, enabling zero-downtime network reconfiguration.
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
- Always use user-defined bridges for application networks. Reserve the default bridge for temporary debugging containers only.
- Specify subnets explicitly with
--subnet. Docker auto-assigns subnets from a private range, but explicit subnets prevent conflicts with corporate VPNs or other network infrastructure. - Use
--ip-rangeto constrain the allocatable pool when you need to reserve addresses for static assignments or external services. - Leverage DNS names instead of IP addresses in application configuration. Docker's embedded DNS at 127.0.0.11 resolves container names automatically on user-defined bridges.
- Isolate environments by placing development, staging, and production containers on separate bridge networks, even on the same host.
- Enable IPAM options to integrate with external IP address management systems via the
--ipam-driverflag when required.
Common Pitfalls
- Default bridge DNS gap: Containers on the default
bridgenetwork cannot resolve each other by name. Developers often test withdocker run(which uses the default bridge unless--networkis specified) and then wonder why service discovery fails. - Port mapping conflicts: Two containers cannot bind to the same host port. If container A uses
-p 8080:80, container B attempting the same mapping will fail with a port allocation error. Use dynamic port mapping (-p 80) or a reverse proxy like Traefik or Nginx to route traffic. - Subnet overlap: If the bridge subnet overlaps with the host's physical network or VPN routes, traffic to those ranges may be incorrectly routed into the bridge. Always check
ip routeon the host before choosing a subnet. - Container-to-container access assumptions: Containers on different bridge networks cannot reach each other unless you explicitly connect one container to both networks (which creates a multi-homed container) or use inter-network routing at the host level.
- ICMP blocking: Some cloud platforms and firewall rules block ICMP, so
pingtests between containers may fail even though TCP connectivity works. Always verify connectivity with protocol-specific tools likecurlornc.
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
- Maximum performance: No bridge overhead, no veth pairs, no NAT translation. Network throughput and latency approach bare-metal performance, critical for latency-sensitive applications like high-frequency trading systems or real-time media processing.
- Full host network access: The container can bind to all host interfaces (including
localhost,0.0.0.0, and physical NICs), use raw sockets, and manipulate firewall rules — necessary for network monitoring tools, VPN containers, or custom load balancers. - Simplified port binding: Ports exposed by the container appear directly on the host with no explicit port mapping needed. If your application listens on port 5432, it is immediately reachable on
host-ip:5432.
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
- Reserve host mode for specific use cases: network monitoring, VPN termination, system-level proxies, and performance-critical applications that genuinely cannot tolerate bridge overhead. For most workloads, bridge mode with proper tuning is sufficient.
- Port conflict prevention: Since the container binds directly to host ports, document which ports each host-mode container uses and enforce exclusivity. Two host-mode containers cannot both listen on port 80; one will fail to start.
- Combine with process-level isolation: Host mode shares the network namespace but not the PID, mount, or user namespaces. Use
--read-onlyroot filesystem, minimal capabilities, and non-root users to limit blast radius. - Use only on Linux: Host networking is not supported on Docker Desktop for Mac or Windows due to platform virtualization layers. On macOS, even with
--network host, the container shares the VM's network namespace, not the Mac host's. - Audit listening interfaces: Applications in host-mode containers should bind to specific interfaces (
127.0.0.1for local-only services, or a specific private IP) rather than0.0.0.0to avoid unintended exposure.
Common Pitfalls
- Port conflicts between host-mode containers: If container A binds to port 5432 in host mode, container B attempting the same will crash. Unlike bridge mode, there is no namespace isolation to prevent collisions.
- Accidental exposure: A container in host mode listening on
0.0.0.0:6379exposes Redis to the entire network the host is connected to, including public interfaces. Always bind to specific interfaces when possible. - Broken assumptions in multi-container setups: Developers sometimes switch a single container to host mode while leaving others on bridge networks, then expect them to communicate via
localhost. Host-mode containers see hostlocalhost; bridge-mode containers have their own isolated loopback. - Docker Desktop confusion: On macOS and Windows,
--network hostdoes not give access to the laptop's network interfaces — it shares the hidden Linux VM's network stack. This leads to debugging dead ends when developers expect to see their corporate WiFi interface inside the container. - Firewall bypass: Host-mode containers bypass Docker's userland proxy and iptables rules. If you rely on Docker's port mapping for access control, host-mode containers will sidestep those restrictions entirely.
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
- Multi-host container networking: Without overlay, containers on separate hosts can only communicate via host port mapping and external routing. Overlay provides direct container-to-container IP connectivity across hosts.
- Swarm service mesh: In Swarm mode, overlay networks integrate with Docker's routing mesh to provide automatic load balancing across service replicas, regardless of which node receives the initial request.
- Encrypted transport: The
--opt encryptedflag enables automatic IPsec encryption of all overlay traffic between nodes, protecting inter-service communication without application-level TLS. - Service discovery at scale: Docker's built-in DNS works across overlay networks, letting containers on different hosts resolve service names to the correct virtual IP.
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
- Encrypt overlay traffic in production: Always use
--opt encryptedwhen overlay traffic traverses networks you don't fully control. The performance overhead is minimal (typically 5-10%) and the security benefit is substantial. - Use the
--attachableflag judiciously: It allows non-Swarm containers to join the overlay, which is useful for debugging but weakens the Swarm service model. Restrict attachable overlays to development and troubleshooting contexts. - Plan subnet space carefully: Overlay subnets must not overlap with existing corporate networks, bridge networks, or other overlays. Create a network addressing plan document that allocates non-overlapping ranges for each overlay.
- Monitor VXLAN overhead: VXLAN encapsulation adds approximately 50 bytes of header overhead per packet. For throughput-sensitive applications, benchmark performance with and without overlay and consider macvlan if latency is unacceptable.
- Enable swarm-wide encryption: Beyond network encryption, enable Swarm control-plane encryption with
docker swarm update --task-history-limit 0and always rotate join tokens regularly. - Use overlay for stateful services cautiously: Databases and stateful services on overlay networks are susceptible to split-brain scenarios during network partitions. Always configure proper quorum mechanisms and avoid spreading database replicas across high-latency geographical links on a single overlay.
Common Pitfalls
- Forgetting
--attachable: By default, overlay networks created for Swarm services do not allow standalone containers to attach. Attemptingdocker run --network my-overlayon an unattachable overlay fails with a confusing error. Always add--attachableduring creation if non-service containers need access. - KV store dependency (legacy): In older Docker versions (pre-1.12), overlay networks required an external key-value store like Consul or etcd. Modern Docker Swarm handles this internally, but legacy documentation still circulates and causes confusion.
- Subnet exhaustion: Each overlay network allocates a default /24 subnet. Creating many overlays without specifying
--subnetcan exhaust the default 172.30.0.0/16 allocation pool. Always specify subnets explicitly. - Cross-platform incompatibility: Overlay networking between Linux and Windows Swarm nodes has specific version requirements and may require disabling encryption. Check Docker's cross-platform compatibility matrix before deploying mixed-OS swarms.
- Firewall interference: Overlay VXLAN traffic uses UDP port 4789. Corporate firewalls or cloud security groups that block this port silently break overlay communication. Ensure this port (and the Swarm control plane ports 2377 and 7946) are open between all Swarm nodes.
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
- Legacy application compatibility: Some legacy systems require containers to appear as first-class network citizens — with their own MAC addresses — for licensing, monitoring, or compliance reasons. Macvlan satisfies these requirements where NAT and overlay abstractions fail.
- Direct physical network integration: Containers can obtain IP addresses from the organization's DHCP server, respond to ARP requests directly, and participate in VLAN-tagged networks without any Docker-level NAT or proxying.
- Maximum performance with minimal overhead: Macvlan operates at the kernel level with near-zero processing overhead, delivering line-rate throughput ideal for network appliances, software routers, or high-throughput data processing containers.
- VLAN trunking: The macvlan driver supports 802.1Q VLAN tagging at the sub-interface level, allowing containers to be placed into specific VLANs for network segmentation that aligns with existing infrastructure.
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
- Use macvlan only when bridge or overlay cannot satisfy requirements. Macvlan bypasses Docker's network isolation model, so you lose the security boundary that bridge networks provide between host and container.
- Reserve IP addresses carefully: Use
--ip-rangeand--aux-addressto carve out dedicated pools for containers. Without these, Docker might allocate an IP that conflicts with an existing physical device on the network. - Coordinate with network administrators: Macvlan containers consume MAC addresses on the physical network. Some enterprise switches have MAC table limits or security policies (like port security) that may trigger alerts or block ports when unfamiliar MACs appear.
- Use VLAN trunking for segmentation: Rather than placing containers on the main data VLAN, create dedicated VLANs for container workloads. This preserves existing network policies and prevents container traffic from mixing with general corporate traffic.
- Enable promiscuous mode on the parent interface: Many hypervisors and cloud platforms disable promiscuous mode by default, which macvlan requires. Verify that the underlying infrastructure allows it, or macvlan connectivity will silently fail.
- Document the host-container communication gap: By design, a macvlan container cannot communicate directly with its parent host via the macvlan interface. If the container needs to reach a service on the host, configure a separate bridge network or use a dedicated internal macvlan subnet for host-container traffic.
Common Pitfalls
- Host-container communication black hole: A container on a macvlan network cannot ping or connect to the host's IP address on the parent interface. This is a fundamental kernel limitation of macvlan bridge mode — traffic between the container and the host is intentionally not switched. The workaround is to add a dedicated macvlan sub-interface on the host for host-container traffic or use a separate bridge network for management.
- MAC address table overflow: Each macvlan container consumes a MAC address in the physical switch's forwarding table. In large deployments without coordination, this can exhaust switch resources or trigger security alerts from network monitoring systems.
- DHCP exhaustion: If containers obtain IPs via DHCP and you don't constrain the pool, you may exhaust the corporate DHCP scope. Always use static IP assignments or a dedicated DHCP scope for container workloads.
- Promiscuous mode required: Macvlan requires the parent interface to operate in promiscuous mode (or at least accept MAC addresses not assigned to the physical NIC). On cloud platforms like AWS EC2, promiscuous mode is blocked by the hypervisor, rendering macvlan unusable. Test early in the deployment cycle.
- Docker Desktop limitations: Macvlan is not functional on Docker Desktop for Mac or Windows because the Linux VM's virtual NICs don't have access to the physical network in a way that macvlan requires. This is a common source of frustration during local development.
- NAT-less exposure: Macvlan containers are directly exposed to the physical network with no Docker firewall mediation. If a container runs a vulnerable service, there is no iptables boundary between it and the rest of the network. Apply host-level firewall rules or container-level security hardening.
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.