Docker Networking: From Bridge to Overlay

Master Docker's networking models—bridge, host, overlay, and macvlan—for connecting containers across hosts and distributed applications.

published: reading time: 20 min read author: GeekWorkBench updated: June 17, 2026
Quick Summary

Docker networking connects containers on one host or across Swarm nodes, with each driver trading isolation, service discovery, and reachability differently. This guide compares user-defined bridges, host networking, overlay, and macvlan, including port publishing, MTU, and address planning. Its troubleshooting and security checks help trace DNS or cross-host failures and limit unintended exposure.

Docker Networking: From Bridge to Overlay

Introduction

Containers are isolated by design, but most applications need them to communicate with each other, external services, or containers on other hosts. Docker provides several network drivers that trade isolation, discoverability, and performance differently.

This guide compares bridge, host, overlay, and macvlan networking, then covers DNS discovery, troubleshooting, performance, and security. It also covers production failures such as misconfigured service discovery and blocked overlay paths.

Bridge Networks for Single-Host Containers

The bridge network is the default. When you run a container without specifying a network, Docker connects it to the default bridge.

How the Default Bridge Works

Docker creates a Linux bridge called docker0 on the host. Containers get virtual ethernet interfaces connected to this bridge. The bridge assigns containers IP addresses from a private subnet (typically 172.17.0.0/16).

Host
+-------------------+
|  docker0 bridge   |  172.17.0.1
|                   |
|  +-------------+  |
|  | container A |  |  172.17.0.2
|  +-------------+  |
|  +-------------+  |
|  | container B |  |  172.17.0.3
|  +-------------+  |
+-------------------+

Containers on the default bridge can reach each other by IP address, but not by container name. DNS resolution by name only works on custom bridge networks.

Custom Bridge Networks

Create your own bridge network to get automatic DNS resolution:

docker network create --driver bridge my_network

Or in Docker Compose:

services:
  web:
    build: .
    networks:
      - frontend

  api:
    build: ./api
    networks:
      - frontend

networks:
  frontend:
    driver: bridge

Now web can reach api at api:3000 (or whatever port api exposes). Docker’s embedded DNS resolver handles this automatically.

DNS Resolution Flow on Custom Bridge

sequenceDiagram
    participant Web as Web Container
    participant Resolver as Docker DNS Resolver
    participant API as API Container

    Web->>Resolver: Lookup api
    Note over Resolver: Resolve name on the connected network
    alt Container name found
        Resolver-->>Web: Return API container IP
        Web->>API: Send request to API:3000
    else Container name not in cache
        Resolver->>Resolver: Forward external lookup to configured DNS
        Resolver-->>Web: Return resolved IP
        Web->>API: Send request
    end

Port Mapping

Containers on bridge networks are isolated from the host by default. To expose a container port to the host:

# Map host port 8080 to container port 80
docker run -d -p 8080:80 nginx:latest

# Map multiple ports
docker run -d -p 8080:80 -p 8443:443 nginx:latest

# Bind to specific host interface only
docker run -d -p 127.0.0.1:8080:80 nginx:latest

# Random host port
docker run -d -P nginx:latest  # Docker assigns random ports

In Docker Compose:

services:
  web:
    image: nginx:latest
    ports:
      - "8080:80" # Host:Container
      - "127.0.0.1:8081:80" # Localhost only
      - "3000-3010:3000" # Port range

Host Networking Performance

The host network driver removes network namespace isolation entirely. The container shares the host’s network stack:

docker run --network host nginx:latest

On the default bridge, Docker adds a layer of indirection through the virtual ethernet and bridge. With host networking, there’s no indirection. The container binds directly to the host’s network interfaces.

This can help workloads that need direct access to host interfaces or have measured networking overhead. Benchmark the workload on the target host before choosing host mode; the result depends on the driver, platform, and traffic pattern.

The tradeoff is reduced network isolation and shared host ports. If two processes try to bind the same address and port, one fails. Host networking is supported on Linux; Docker Desktop support depends on its version and configuration.

services:
  nginx:
    image: nginx:latest
    network_mode: host
    # No port mapping needed - container uses host ports directly

Overlay Networks for Multi-Host

When you have multiple Docker hosts, containers on different hosts need a way to communicate. The overlay network driver creates a distributed network across hosts, making all containers appear on the same logical network regardless of which host they run on.

Overlay networks require Docker hosts to participate in Swarm mode so Docker can coordinate network state. This also applies when standalone containers attach to an overlay; an external key-value store is not the setup path.

Docker Swarm Mode Overlay

Swarm mode includes built-in overlay networking:

# Initialize swarm
docker swarm init

# Create an overlay network (works across all swarm nodes)
docker network create --driver overlay my_overlay

Swarm services attached to the network can communicate across hosts. To attach standalone containers, create the network with --attachable; Swarm handles VXLAN tunneling and distributed network state.

Standalone Containers on an Overlay

Standalone containers can join an overlay network when it is attachable, but every participating Docker host must first join the same Swarm:

# Run on a manager, then join other Docker hosts to this swarm
docker swarm init

docker network create --driver overlay --attachable my_overlay

docker run -d --name api --network my_overlay myapi:latest

A Compose file can attach services to that existing network. Create the overlay through a Swarm manager first, then mark it external so Compose does not try to create a local network:

services:
  api:
    image: myapi:latest
    networks:
      - my_overlay

networks:
  my_overlay:
    external: true

How Overlay Networking Works

Overlay uses VXLAN (Virtual Extensible LAN) to encapsulate container traffic. Each container gets an IP on the overlay network. When container A sends a packet to container B on a different host:

  1. Container A sends packet to the overlay interface
  2. The host’s Docker overlay driver encapsulates the packet in VXLAN
  3. The encapsulated packet travels over the physical network to host B
  4. Host B’s overlay driver decapsulates and delivers to container B

This encapsulation adds small overhead but enables seamless multi-host networking without needing your network team to assign new IP addresses.

VXLAN Encapsulation Flow

Container A -> Host A: Send packet to Container B IP
Host A -> Host A: Encapsulate in VXLAN (UDP port 4789)
Host A -> Network: Forward encapsulated packet
Network -> Host B: Route to Host B
Host B -> Host B: Decapsulate VXLAN
Host B -> Container B: Deliver original packet

Macvlan for Legacy Integration

Some applications expect to appear as physical machines on the network, with their own MAC address. They may rely on LAN addressing or inspect traffic as if it came from a real NIC.

Macvlan gives each container a distinct MAC address and attaches it directly to the physical network:

# Create macvlan network attached to eth0
docker network create \
    --driver macvlan \
    --subnet=192.168.1.0/24 \
    --gateway=192.168.1.1 \
    -o parent=eth0 \
    my_macvlan
services:
  legacy_app:
    image: legacy:latest
    networks:
      - my_macvlan

networks:
  my_macvlan:
    driver: macvlan
    driver_opts:
      parent: eth0
    ipam:
      config:
        - subnet: 192.168.1.0/24
          gateway: 192.168.1.1

Docker allocates the container IP from the subnet configured for the macvlan network; it does not request a lease from the LAN DHCP server. Reserve the range for Docker or exclude it from DHCP to avoid address conflicts.

The container appears as a separate device to the LAN, so switches and cloud networks must allow multiple MAC addresses on the parent interface. On Linux, a macvlan container cannot communicate with its host through the parent interface by default. Add a host-side macvlan interface if that path is required.

When to Use Each Docker Network Driver

Choose the least complex driver that fits the traffic path:

Driver Use it when Cost or constraint
User-defined bridge Containers on one Docker host need isolation and service-name DNS. Traffic stays on one host; published ports still expose selected services.
Host A Linux workload needs the host network stack and measurements show bridge overhead matters. Removes network namespace isolation and shares host ports.
Overlay Swarm services need to communicate across Docker hosts. Requires Swarm membership and node-to-node reachability; VXLAN adds encapsulation overhead.
Macvlan A legacy system or LAN device must see the container as a separate network endpoint. Requires address planning and network support for multiple MAC addresses; host access needs extra configuration.

When Not to Use a Docker Network Driver

  • Avoid host networking when services need independent port spaces or strong network namespace isolation.
  • Avoid overlay networking for a single-host application; a user-defined bridge has fewer moving parts.
  • Avoid macvlan when the physical or cloud network blocks extra MAC addresses, or when you cannot reserve a non-overlapping address range.
  • Avoid the default bridge for service-to-service traffic that depends on stable names; use a user-defined bridge instead of pinning container IPs.

DNS-Based Service Discovery

Docker’s embedded DNS provides name resolution for containers on user-defined networks. This is how containers find each other without hardcoded IP addresses.

Automatic Name Resolution

On a custom bridge or overlay network, Docker registers container names as DNS entries:

services:
  db:
    image: postgres:15-alpine
    networks:
      - backend

  web:
    image: myapp:latest
    networks:
      - backend
    depends_on:
      - db

The web container can reach the database at db:5432, using its Compose service name. If clients need another name, add a network alias explicitly instead of relying on the container hostname.

Custom DNS Entries with Aliases

You can add DNS aliases for a service:

services:
  api:
    image: myapi:latest
    networks:
      backend:
        aliases:
          - api.internal
          - api-service

Now other containers can reach the API at api, api.internal, or api-service.

DNS Search Domains

services:
  web:
    image: myapp:latest
    dns_search: "example.com"

The container appends example.com to unqualified hostnames. If the container looks up database, it becomes database.example.com.

Network Troubleshooting

When containers can’t communicate, here’s how to debug.

Check Container Network Configuration

# Inspect container network settings
docker inspect -f '{{json .NetworkSettings.Networks}}' mycontainer | jq

# Get container IP address
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mycontainer

# Check which networks a container is on
docker inspect mycontainer | jq '.[0].NetworkSettings.Networks'

Test Network Connectivity

# Get a shell in the container
docker exec -it mycontainer sh

# From inside the container, test connectivity
ping api
curl http://api:3000
nslookup api  # Check DNS resolution
netcat -zv api 3000  # Check port connectivity

Check Network Driver Status

# List all networks
docker network ls

# Inspect a network
docker network inspect bridge

# Check for orphaned networks (not used by any container)
docker network prune

Common Issues

Container can’t reach another container by name:

  • Are they attached to the same user-defined network? Run docker network inspect <network> to check.
  • Is the expected service name or network alias configured? Test DNS from the container and compare with the current endpoint IP.

Container can’t reach external networks:

  • Check iptables rules on the host: iptables -L -n
  • Is IP forwarding enabled? cat /proc/sys/net/ipv4/ip_forward

Port mapping not working:

  • Is something else using the host port? ss -tlnp | grep 8080
  • Is the container actually listening? docker logs mycontainer

Observability Checklist

  • Connectivity: Run synthetic DNS and TCP checks between critical containers. Track failures by source service, destination, and Docker network so a name-resolution issue is easy to separate from a port or route failure.
  • Host network health: Monitor interface bytes, packet errors and drops, plus connection-tracking table usage. Alert before conntrack or interface limits cause new connections to fail.
  • Overlay health: Watch Swarm node membership and quorum, VXLAN reachability, packet loss, and MTU-related fragmentation. Test cross-host paths after network or cluster changes.
  • Port exposure: Record published ports and bind addresses at deployment. Alert on unexpected ports exposed to all host interfaces, and audit changes to network membership.
  • Request context: Include container/service identity, network, and connection outcome in logs. Propagate request or trace IDs across services without logging payloads or credentials.

Network Performance Tuning

For high-throughput applications, network performance matters.

Increase Connection Tracking Table Size

On busy Docker hosts, the nf_conntrack table can fill up:

# Check current usage
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max

# Increase max if needed (set in /etc/sysctl.conf for persistence)
echo 1048576 > /proc/sys/net/netfilter/nf_conntrack_max

Adjust MTU for Overlay Networks

Overlay encapsulation reduces the usable container MTU. A common IPv4 VXLAN path adds about 50 bytes of overhead; IPv6 underlays or encryption can add more. With a standard 1500-byte underlay, 1450 is a common starting point:

docker network create \
    --driver overlay \
    --opt com.docker.network.driver.mtu=1450 \
    my_overlay

For a jumbo-frame underlay, choose an overlay MTU from the actual path MTU and encapsulation overhead instead of reusing 1450. Verify the end-to-end path after changing it.

Use DNS Cache for High Query Volumes

If your containers make many DNS queries, configure a caching resolver that they can reach on the network. Point Docker’s dns setting at that resolver’s reachable address:

services:
  app:
    image: myapp:latest
    dns:
      - 10.0.0.53 # Address of a reachable caching resolver

Production Failure Scenarios

Docker networking fails in ways that aren’t always obvious. Here are the most common issues.

Overlay Network Connectivity Failure

A Swarm overlay can fail across hosts if required node-to-node ports are blocked, the hosts cannot reach each other, or Swarm membership is unhealthy. Loss of manager quorum prevents control-plane changes; it does not by itself prove that every existing data path has stopped.

Symptoms: containers on different hosts cannot reach each other while local connectivity still works.

Diagnosis:

# Check swarm node availability
docker node ls

# Inspect the overlay network
docker network inspect my_overlay

# Check host firewall rules and UDP 4789 reachability between nodes

Check Swarm membership, firewall rules, and MTU across the data path before changing the network.

DNS Resolution Failure After Container Replacement

A replacement container may have a different IP address. Applications should resolve the service name through Docker DNS instead of retaining the old address, and both endpoints must share a user-defined network.

Symptoms: a lookup for the expected service name fails, or the application keeps connecting to an old IP.

Diagnosis:

# Check container DNS configuration
docker exec mycontainer cat /etc/resolv.conf

# Check the service name from inside the client container
docker exec mycontainer nslookup api

Check that both containers are attached to the same user-defined network and that the client uses the service name or configured network alias. Docker’s embedded resolver is available to containers on user-defined networks.

IP Address Pool Exhaustion

A Docker network can run out of addresses when its configured IPAM pool is too small for its attached endpoints. Frequent container recreation alone should release addresses when endpoints are removed; check for still-attached containers and the network subnet before resizing the pool.

Symptoms: docker: Error response from daemon: could not find an available IP address.

Fix: remove unused endpoints and choose a subnet sized for the expected concurrent endpoints, checking that it does not overlap with host or routed networks.

Anti-Patterns

These patterns cause problems in production. Avoid them.

Using Default Bridge

The default bridge (docker0) doesn’t provide DNS-based service discovery by container name. Containers must reference each other by IP address, which changes on restart.

Always create custom bridge networks:

docker network create --driver bridge my_custom_bridge

Connecting Containers Across Multiple Networks Improperly

A container on network A can’t reach a container on network B unless you explicitly connect them:

# Start on the first network, then attach the second explicitly
docker run -d --name myapp --network network_a myapp
docker network connect network_b myapp

Ignoring Port Security

Exposing ports to all interfaces (-p 8080:80) makes your container reachable from any network interface. Use -p 127.0.0.1:8080:80 when you only need local access.

The risk changes depending on where you’re deploying:

Exposure level Binding Risk
8080:80 All interfaces Container reachable from external networks
127.0.0.1:8080:80 Localhost only Container only reachable from the host itself
192.168.1.10:8080:80 Specific host IP Container reachable only through interfaces assigned that address

Use a host IP assigned to the interface you want to expose. Docker’s -p syntax takes an IP address, not an interface name such as eth0.

In cloud or multi-tenant environments, binding to 0.0.0.0 instead of a specific interface can expose internal services to the internet. Databases, admin panels, and internal APIs tend to get hit first.

Find over-exposed ports on running containers:

# List all port mappings with their bind addresses
docker ps --format '{{.Names}}: {{.Ports}}' | grep -v 127.0.0.1

# Check which containers bind to all interfaces
docker inspect --format='{{.Name}} {{range $k,$v := .NetworkSettings.Ports}}{{range $i,$j := $v}}{{$j.HostIp}}:{{$j.HostPort}} {{end}}{{end}}' $(docker ps -q) | grep '0.0.0.0:'

If you see 0.0.0.0: in the output, that container is accepting connections from any network interface. Review whether it should be restricted to 127.0.0.1 or a specific interface.

Not Cleaning Up Unused Networks

Unused networks accumulate and cause confusion. Regularly run docker network prune to clean up networks not used by any container.

Unused networks are easy to create and easy to forget. Each one consumes a small amount of memory and kernel resources. They also hide the actual network topology, making it harder to reason about which containers can reach each other.

How they pile up:

  • Running docker network create for testing and forgetting to remove it
  • Leaving networks from old Docker Compose stacks that were docker compose down without --remove-orphans
  • Networks created by CI pipelines or local dev scripts that never get cleaned up

Identify orphaned networks before they cause problems:

# List networks with no attached containers
docker network ls --format '{{.Name}}' | while read net; do
  count=$(docker inspect --format='{{len .Containers}}' "$net" 2>/dev/null || echo 0)
  [ "$count" -eq 0 ] && echo "$net is orphaned"
done

# Show all networks with their container counts
docker network ls -q | xargs -I {} docker network inspect {} --format='{}: {{len .Containers}} containers'

Fix orphaned networks:

# Remove all unused networks
docker network prune

# Remove a specific unused network
docker network rm my_unused_network

For CI/CD pipelines, add docker network prune -f at the end of jobs or use a scheduled cleanup job. In local development, docker compose down --remove-orphans removes networks created by a compose file but no longer needed.

Security and Compliance Notes

Treat network membership as an access boundary. Put databases and administrative APIs on backend networks, and attach each service only to the networks it needs. Use an internal network for backends that do not need outbound access. Container names and IP addresses help route traffic; they do not authenticate a service or authorize an operation.

Host networking and macvlan expose containers to more of the host or LAN, so use them only when the workload requires that access. Encrypt sensitive service-to-service traffic with TLS or mutual TLS, and restrict access to the Docker daemon and socket to trusted operators. For compliance reviews, document which services can exchange which data, keep an audit trail of network and port changes, and retain only the connection metadata required by policy.

Quick Recap Checklist

  • Choose the network driver based on host topology, isolation, and legacy requirements.
  • Use a custom bridge for single-host containers that need service-name DNS.
  • Publish only the ports that external clients need to reach.
  • Use overlay networking for multi-host Swarm services and monitor cluster health.
  • Diagnose connectivity by checking network membership, DNS, routes, ports, and MTU.
  • Remove unused networks so the actual topology stays clear.

Interview Questions

1. How does a custom bridge differ from Docker's default bridge?
A custom bridge provides automatic DNS-based discovery between attached containers, so services can connect by name. The default bridge generally relies on manual linking or IP addresses for container-to-container discovery.
2. When should a team use an overlay network?
Use an overlay when services need to communicate across multiple Docker hosts, such as in a Swarm deployment. It adds encapsulation and cluster dependencies, so it is unnecessary for containers that stay on one host.
3. What does host networking trade away?
It removes the container's separate network namespace, which can reduce overhead and provide direct host network access. The container also shares host ports, so port collisions are possible and network isolation is reduced.
4. What should you check when containers cannot reach each other by name?
Confirm both containers are attached to the same user-defined network, then test name resolution and port reachability. For overlay networks, check Swarm membership and MTU as well; a replacement container should be reached through its service name rather than a retained IP address.
5. What is required for a standalone container to join a Docker overlay network?

Expected answer points:

  • The Docker hosts must participate in the same Swarm, and an overlay network is created through a Swarm manager.
  • The overlay must be created with `--attachable` for standalone containers to join it.
  • Participating hosts still need the required node-to-node connectivity, including VXLAN traffic where used.
6. What is the difference between publishing a container port on all interfaces and binding it to localhost?

Expected answer points:

  • `-p 8080:80` publishes the container port on the host's available interfaces by default.
  • `-p 127.0.0.1:8080:80` limits the published host port to loopback, so remote hosts cannot reach it through the host's external interfaces.
  • Choose the bind address based on intended reachability and retain firewall controls for exposed services.
7. Why might a macvlan container be unreachable from its Docker host?

Expected answer points:

  • On Linux, the host cannot communicate with a macvlan container through the parent interface by default.
  • If that path is required, configure a host-side macvlan interface on the appropriate subnet.
  • Also check address reservations and whether the physical or cloud network permits multiple MAC addresses on the parent interface.
8. How does connection-tracking exhaustion differ from Docker IP address pool exhaustion?

Expected answer points:

  • Connection-tracking exhaustion prevents the host from tracking new network flows; IP pool exhaustion prevents Docker from assigning an endpoint address on a network.
  • Check conntrack current and maximum counts for flow pressure, and inspect attached endpoints and the network subnet for IPAM exhaustion.
  • They need different remedies: address-pool planning does not free conntrack entries, and increasing conntrack capacity does not add network addresses.

Further Reading

Conclusion

A custom bridge is usually enough on one host. Add overlay only when services span hosts, and use network membership, DNS lookup, and port tests to narrow down a failed connection.

Category

Related Posts

Container Images: Building, Optimizing, and Distributing

Learn how Docker container images work, layer caching strategies, image optimization techniques, and how to publish your own images to container registries.

#docker #containers #devops

Container Registry: Image Storage, Scanning, and Distribution

Set up and secure container registries for storing, scanning, and distributing container images across your CI/CD pipeline and clusters.

#containers #docker #registry

Docker Fundamentals

Learn Docker containerization fundamentals: images, containers, volumes, networking, and best practices for building and deploying applications.

#docker #containers #devops