Docker Networking: Bridge vs Macvlan vs Overlay - 夜莺博客

Docker Networking: Bridge vs Macvlan vs Overlay

Choosing the wrong Docker network type causes classic headaches: containers that can't see each other, NAT overhead where none is needed, or overlay complexity on a single host. The default bridge works for most single-host stacks, macvlan makes containers look like native LAN devices, and overlay connects containers across multiple Swarm hosts. This tutorial walks through all three with concrete commands, pros and cons, and a troubleshooting section for the most common cross-host connectivity problems.

The Five Drivers at a Glance

Before the detail, it is worth fixing the taxonomy. Docker ships five built-in network drivers, and every one of them answers a different question about how packets leave the container:

  • bridge — the container gets a private address on a virtual switch inside the host. Traffic leaving the host is NATed. This is the default.
  • host — the container shares the host's network namespace directly. No isolation, no NAT, no port mapping, and the container can bind any port the host has free.
  • none — the container gets a loopback interface and nothing else. Useful for batch jobs you attach to a network later.
  • macvlan / ipvlan — the container gets an address on your real LAN segment with its own MAC (macvlan) or a shared MAC (ipvlan). It looks like a physical device to the rest of the network.
  • overlay — a VXLAN tunnel between Docker hosts, giving containers an address space that spans machines. Requires Swarm or an external key-value store.

Pick host networking when a single container needs maximum throughput and port collisions are not a concern; pick bridge for the other ninety percent of single-host work; pick macvlan when other devices on the LAN must initiate connections to the container; pick overlay when the containers must span hosts.

1. Bridge Networks: Single-Host Stacks

A bridge network lives on one host; the default one uses the 172.17.0.0/16 range and the docker0 virtual interface. Create a dedicated network for a monitoring stack:

docker network create monitor-net
docker run -d --network monitor-net --name prometheus prom/prometheus
docker run -d --network monitor-net --name grafana grafana/grafana

Pros: zero extra config, fast packet path, solid default isolation. Cons: host-only, subnet conflicts possible with multiple bridges, hair-pin NAT for published ports.

The one decision that matters here is user-defined bridge versus the legacy default bridge. On a user-defined network Docker runs an embedded DNS resolver at 127.0.0.11, so containers can reach each other by name — grafana can reach http://prometheus:9090 with no links, no --link flags and no IP addresses baked into config files. On the default bridge, name resolution only works through the deprecated --link mechanism. That single feature is why you should never let a stack share the default bridge.

You can also pin the subnet and gateway when the defaults collide with a VPN or a corporate range:

docker network create --subnet 172.30.0.0/24 --gateway 172.30.0.1 \
  -o com.docker.network.bridge.name=br-monitor monitor-net

For containers that need to talk to one another on many ports, bridge networking has no serious downside. The cost only appears when you publish a port: -p 8080:80 goes through a NAT rule and a userland proxy in some configurations, adding a small amount of latency and hiding the container's real source IP from anything that logs it.

2. Macvlan: Containers as LAN Devices

Each macvlan container gets its own MAC and sits directly on your physical LAN — no port mapping, no NAT:

docker network create -d macvlan --subnet=10.1.149.0/24 --gateway=10.1.149.1 -o parent=ens192 lab-macvlan
docker run -d --network lab-macvlan --ip 10.1.149.150 --name zigbee2mqtt my-zigbee-image

Note that Docker's macvlan IPAM does not do DHCP — you must either run a DHCP client inside the container, omit --subnet/--gateway and run dhclient/dhcpcd in the container, or use a DHCP IPAM plugin.

The parent option names the interface the container is bridged onto. If that interface carries multiple VLANs, add the VLAN sub-interface as the parent instead — for example -o parent=ens192.30 — and make sure the switch port trunks that VLAN. Docker will not create the sub-interface for you.

Here is the trap that catches everyone once: the Docker host cannot reach its own macvlan containers. The host sends traffic out ens192 to an address that lives on a macvlan interface derived from ens192; the kernel sees the destination as local and never puts the frame on the wire. The fix is a shim interface:

ip link add macvlan-shim link ens192 type macvlan mode bridge
ip addr add 10.1.149.200/32 dev macvlan-shim
ip link set macvlan-shim up
ip route add 10.1.149.150/32 dev macvlan-shim

Also note that many wireless drivers do not support macvlan at all, and that a macvlan container can exhaust the DHCP pool of the segment it sits on. Reserve addresses or use static assignment.

IPvlan: Macvlan Without the MAC Address Explosion

IPvlan is macvlan's sibling and shares the same parent-interface model, but every container uses the parent's MAC address and only the IP differs. That matters in two situations: wireless interfaces, and switches with a MAC address table limit or port security configured. ipvlan mode L2 keeps per-container IPs on the same L2 segment, while L3 mode turns the host into a router for its containers with no broadcast domain at all:

docker network create -d ipvlan --subnet=10.1.149.0/24 --gateway=10.1.149.1 \
  -o ipvlan_mode=l3 -o parent=ens192 lab-ipvlan-l3

3. Overlay: Multi-Host Swarm Networking

docker swarm init --advertise-addr 10.0.0.1
docker network create -d overlay --attachable webnet
docker service create --replicas 3 --name web --network webnet nginx

Overlay uses VXLAN encapsulation, with built-in DNS and load balancing. Cons: CPU overhead, requires Swarm/Kubernetes, and MTU fragmentation is possible — lower the overlay MTU if you see "fragmentation needed" errors.

Two overlay details are worth knowing before you deploy. First, --attachable is what lets ordinary docker run containers join the overlay; without it only services can, which is fine for pure Swarm workloads but blocks one-off debugging containers. Second, in Swarm mode every node participates in the ingress routing mesh, so a published service port is reachable on every node's IP even where no task is running. That is convenient and also a common source of confusion when firewall rules only allow the port on one host.

The overlay data path also has an encryption switch worth choosing deliberately: --opt encrypted wraps VXLAN in IPsec, which costs measurable CPU on high-throughput workloads. On a trusted underlay, leave it off and secure the underlay instead.

Publishing Ports, NAT and the Source IP Problem

On a bridge network, reaching a container from outside the host means publishing a port, and that has consequences worth understanding before they turn into a support ticket:

docker run -d -p 8080:80 --name web nginx
docker port web
docker ps --format '{{.Names}}\t{{.Ports}}'

-p 8080:80 installs a DNAT rule so traffic arriving on host port 8080 is rewritten to the container's address, plus a MASQUERADE rule so the container's replies travel back through the host. The visible effect is that the application inside the container logs the Docker gateway address instead of the client's real address, which quietly breaks rate limiting, geo-blocking and log analysis. Macvlan avoids the whole problem because the container's address is a genuine one on the segment and the client connects to it directly.

Two ways to keep the true source IP on a bridge network: run with host networking where that is acceptable, or terminate a reverse proxy in front of the container and pass the original address through the X-Forwarded-For header (with the proxy's address trusted by the application). Published ports are also where hair-pinning shows up — a container that tries to reach another container through the host's own published address may fail depending on how the rule was installed, while the direct service name resolves correctly every time. Use the service name.

DNS and Service Discovery in Each Mode

Name resolution behaviour differs sharply and is the source of most "it works on my laptop" incidents:

Driver Container-to-container DNS Notes
Default bridge No (only --link) Deprecated; do not build on it
User-defined bridge Yes, by container name / alias Resolver at 127.0.0.11
Macvlan / IPvlan Only via external DNS Appear as LAN devices, so use DHCP/DNS records
Overlay Yes, across hosts, with service VIPs Embedded DNS plus load balancing

Quick Comparison

Feature Bridge Macvlan Overlay
Multi-host No No Yes
Native LAN IPs No (NAT only) Yes (static) No (encapsulated)
Requires orchestration No No Yes (Swarm/K8s)
Performance overhead Minimal Minimal Moderate
Host can reach container Yes No (needs shim) Yes
External clients see real source IP No Yes No

Troubleshooting Cross-Host Connectivity

  • Host can't ping macvlan containers: create a shim interface (ip link add macvlan-shim link eth0 type macvlan mode bridge + IP).
  • Overlay MTU mismatch: create with --opt com.docker.network.driver.mtu=1400. VXLAN adds 50 bytes of overhead, so a 1500-byte underlay cannot carry a 1500-byte inner frame.
  • VXLAN blocked: open UDP 4789 between hosts.
  • Swarm DNS failures: ensure identical cluster-store config in /etc/docker/daemon.json.
  • Ping works but TCP handshakes hang: classic MTU black-hole. Confirm with ping -M do -s 1472 and then clamp the MSS or lower the overlay MTU.
  • Container has an address but no route off-host: on macvlan, the parent interface's switch port must be a trunk carrying the container VLAN untagged, or the container must be tagged to match.
  • Two containers on different bridge networks cannot talk: that is by design. Attach the container to both networks with docker network connect rather than trying to route between bridges.

Handy inspection commands for all of the above:

docker network ls
docker network inspect monitor-net
docker exec -it grafana ip route
docker exec -it grafana cat /etc/resolv.conf
ip -d link show ens192
bridge fdb show | grep ens192

Choosing by Workload

  • Web stack with a reverse proxy on one host — user-defined bridge. Name resolution and isolation for free.
  • Home-automation or IoT containers that devices must reach — macvlan with static IPs, plus a shim so the host can manage them.
  • High-throughput single-purpose service on a dedicated host — host networking, so there is no NAT hop at all.
  • Microservices spread over three or more nodes — overlay under Swarm, or a CNI plugin if Kubernetes is the platform of record.

For Linux server operations around Docker hosts, see our Linux server operations guide, the SONiC GNS3 lab article, and Arista CloudVision AVD with Containerlab. Deeper dives on the same building blocks are in Docker networking drivers compared, Linux network namespaces and veth in practice, and Calico vs Cilium vs Flannel.

原文链接:https://www.virtualizationhowto.com/2025/07/docker-networking-tutorial-bridge-vs-macvlan-vs-overlay-for-home-labs/