Docker Networking Drivers: Bridge, Host, Macvlan and Overlay - 夜莺博客

Docker Networking Drivers: Bridge, Host, Macvlan and Overlay

Docker containers see only a virtual network interface — what that interface connects to depends on the network driver you choose. The default bridge driver works for single-host apps, overlay spans multiple Docker hosts, and macvlan makes containers look like physical devices on your LAN. This guide explains each built-in driver, when to use it, and shows the docker network create commands that provision them.

The Built-in Drivers

  • bridge — default. A software bridge isolates containers on one host; external access only via published ports (-p).
  • host — removes network isolation; the container shares the host's network stack directly.
  • none — no network; use with custom CNI setups.
  • overlay — connects daemons across hosts (Swarm); VXLAN-encapsulated, includes service discovery.
  • macvlan — assigns a real MAC to each container so it appears as a physical device on the parent network.
  • ipvlan — like macvlan but containers share the parent MAC and are addressed by IP/VLAN.

Driver Comparison at a Glance

Driver Scope Container IP visible on LAN Port publishing needed Typical use
bridge (default) Local No Yes Simple single-host apps
bridge (user-defined) Local No Yes Multi-container apps with DNS
host Local Host IP only No High throughput, host tooling
none Local No — Air-gapped or custom netns
overlay Swarm No Via routing mesh Multi-host services
macvlan Local Yes, own MAC No Legacy apps, L2 segmentation
ipvlan Local Yes, shared MAC No Many IPs where MAC limits apply

User-Defined Bridge Networks

User-defined bridges beat the default bridge: containers get automatic DNS resolution by name and can be attached to multiple networks. Create one and run a container on it:

docker network create my-net
docker run -d --name web --network my-net -p 8080:80 nginx
docker network connect my-net my-nginx

Containers on the same user-defined bridge can reach each other on all ports; anything outside needs a published port. To pin the addressing instead of letting Docker pick a random /16, allocate a subnet explicitly — useful when the default pool collides with a VPN or a corporate range:

docker network create \
  --driver bridge \
  --subnet 172.28.0.0/16 \
  --gateway 172.28.0.1 \
  --ip-range 172.28.5.0/24 \
  my-net

docker run -d --name db --network my-net --ip 172.28.5.10 postgres
docker network inspect my-net --format '{{json .Containers}}'

Name resolution works because Docker runs an embedded DNS server at 127.0.0.11 inside every container on a user-defined network; the default bridge has no such resolver, which is why --link existed historically and why it is now deprecated. Two behaviours are worth knowing before you build on it: DNS returns container names and network aliases (use --network-alias for stable service names across recreations), and a container attached to several networks is only resolvable by name from the networks it is actually attached to.

Host and None

The host driver removes the veth pair and the NAT rules entirely; the process binds directly to the host's interfaces. It is the right answer for a load generator, a monitoring agent that must see real interface counters, or a database you want to reach at the host address without a proxy hop. The trade-offs are absolute: no publish mappings, no per-container port isolation, and two containers cannot both bind port 443. In Swarm, --network host is only available with docker run, not as a service network.

docker run --rm --network host nginx
docker run --rm --network none alpine ip addr show

The none driver gives the container a loopback interface and nothing else, which is exactly what you want when networking is provided by something other than Docker (a custom CNI plugin, a kubernetes SDN, or a workload that only talks over its own unix socket).

Overlay Networks for Multi-Host

Overlay lets containers on different Docker hosts talk without OS-level routing — the data plane is VXLAN over the underlay, and DNS-based service discovery resolves web to db wherever they run:

docker network create -d overlay --attachable my-overlay
docker network create -d overlay --opt encrypted --attachable secure-overlay

docker service create --name web --network my-overlay -p 8080:80 nginx
docker network inspect my-overlay --format '{{.Driver}} {{.Scope}}'

Use overlay when applications span several hosts (Swarm services); use bridge when everything runs on one host. Overlay is built on the overlay VXLAN driver over the same L2/L3 primitives described in Linux VXLAN with ip link, and it depends on a routing mesh plus the docker_gwbridge to reach the outside world. Two practical caveats: overlay adds encapsulation overhead, so the underlay must support the resulting MTU (see VXLAN MTU and fragmentation), and --attachable is required if you want standalone docker run containers on the same network as services.

Macvlan: Containers as LAN Devices

Legacy or traffic-sniffing applications sometimes expect a direct physical presence. Macvlan binds containers to a parent interface and gives each a MAC and a routable IP from the external subnet — no NAT, no port mapping:

docker network create -d macvlan \
  --subnet=192.168.86.0/24 \
  --gateway=192.168.86.1 \
  -o parent=eth0 my-macvlan-net

docker run -d --name sniffer --network my-macvlan-net --ip 192.168.86.50 alpine sleep 3600

Two caveats: macvlan containers cannot talk to the Docker host itself (kernel restriction — add a second bridge connection if needed), and the environment must tolerate many MACs on the upstream switch port. The 802.1Q trunk variant uses a parent sub-interface such as eth0.10 to extend a specific VLAN straight into containers. If the upstream switch enforces a MAC-per-port limit, or you simply do not want to burn 30 MAC addresses from a /24, ipvlan is the alternative: containers share the parent interface's MAC and are distinguished by IP only.

ip link add link eth0 name eth0.10 type vlan id 10
docker network create -d macvlan --subnet=10.10.0.0/24 -o parent=eth0.10 vlan10

docker network create -d ipvlan --subnet=10.10.1.0/24 -o parent=eth0 ipvlan-l2
docker network create -d ipvlan --subnet=10.10.2.0/24 -o parent=eth0 -o ipvlan_mode=l3 ipvlan-l3

ipvlan L2 mode behaves like macvlan for most purposes; L3 mode routes between the parent interface and the containers, which lets you reuse the same subnet on multiple hosts. Note that ipvlan and macvlan both bypass the NAT path, so a container with a routable IP is reachable by anything that can route to that subnet — treat the driver choice as an access-control decision, not just a performance one.

Publishing Ports and the Host Boundary

Bridge, overlay and macvlan differ most visibly in how traffic crosses the boundary between a container and the outside world. On a bridge network, -p 8080:80 installs a DNAT rule that rewrites the destination to the container's address, so the client sees the host's IP and the container sees the client's real source address only if you preserve it. Publishing also implicitly creates the rule for every interface unless you bind a specific address, which is why -p 127.0.0.1:8080:80 is the safer default on a host with a public interface.

docker run -d -p 8080:80 nginx
docker run -d -p 127.0.0.1:8081:80 nginx
docker run -d -p 8082:80/udp nginx
docker port web
iptables -t nat -L DOCKER -n | head -20

Two behaviours surprise people. First, Docker's iptables rules bypass ufw deny rules on some distributions, so a port you believe is blocked can be reachable from the internet; the fix is to bind to 127.0.0.1 or set iptables handling explicitly in the daemon configuration rather than layering two firewalls. Second, a container on a macvlan or ipvlan network needs no publishing at all — it already has a routable address — but it also cannot reach the host it runs on, so any service that registers itself with a control plane on the host will fail unless a second interface is attached.

Docker Compose: Pinning the Network

In Compose, networking is declarative: services on the default network get name resolution, and any service published cross-network can reach them by name. Declaring the network explicitly makes the subnet, driver and driver options part of the repository rather than a manual step on each host.

networks:
  frontend:
    driver: bridge
    ipam:
      config:
        - subnet: 172.28.0.0/16
  backend:
    driver: bridge
    internal: true

services:
  web:
    image: nginx
    networks: [frontend, backend]
    ports:
      - "127.0.0.1:8080:80"
  db:
    image: postgres
    networks: [backend]

The internal: true flag on the backend network removes its gateway, so containers attached to it can talk to each other but cannot reach the internet or the host — a cheap and very effective segmentation primitive for a database tier. Note that once a Compose project is up, docker network inspect <project>_backend shows the actual subnet in use, which is the fastest way to prove that two containers really share a network rather than merely appearing to.

Which Driver Should You Use?

  • Single host, simple app → default or user-defined bridge.
  • Performance, full host visibility → host.
  • Swarm / multi-host → overlay.
  • Containers must look physical / VLAN segmentation at L2 → macvlan (or ipvlan where MAC limits apply).

Troubleshooting

Symptom Likely cause Check
Container cannot resolve another container by name Both are on the default bridge, or not on the same network docker inspect --format '{{json .NetworkSettings.Networks}}'
Host cannot reach a macvlan container Kernel macvlan/802.1Q isolation Add a second, bridge-connected interface for management
Published port does not work but the container is fine Bind address mismatch, or UFW/firewall ordering with the DOCKER chain docker port; iptables -t nat -L -n
Overlay DNS resolves but packets are dropped Underlay MTU too small for VXLAN overhead ping with large payload; adjust MTU on the overlay
Duplicate subnet error after a VPN connects Default bridge pool overlaps the corporate range docker network ls; re-create with an explicit --subnet
docker network ls
docker network inspect my-net
docker network prune
docker exec -it web getent hosts db
docker exec -it web ip -br addr
docker exec -it web bridge fdb show | head

For the underlying L2 concepts Docker builds on, see Linux VLAN tagging with ip link and the bridge-VLAN behavior in MikroTik bridge VLAN filtering. If you are running containers at cluster scale rather than single-host, the driver choice is usually replaced by a CNI plug-in — see Kubernetes CNI compared: Calico vs Cilium vs Flannel — and for lab topologies of network devices themselves, Containerlab reuses these same Docker primitives.

原文链接:https://docs.docker.com/engine/network/