Skip to main content
RunBook Academy

Docker & ContainersVII · NetworkingBridge networking

Bridge networks — the default and how to use them well

Intermediate⏱ ~22 min

What you'll learn

  • Create user-defined bridge networks
  • Configure MTU, subnet, gateway, and iptables settings
  • Choose the right network driver per workload

Prerequisites

Verified against Docker Engine 29.x · Docker Engine 28.x · Docker Compose 2.x · containerd 2.x · runc 1.2.x · BuildKit 0.20+ · Linux kernel 5.15+ · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · 2026-08-09

Not yet marked complete on this device.

The default bridge network exists for legacy compatibility with the very first versions of Docker. It has a number of problems that user-defined bridges fix. You should almost always use a user-defined network.

The problems with the default bridge

  • No automatic DNS resolution between containers. Containers on the default bridge can reach each other by IP only.
  • All containers are on the same L2 segment. No isolation between unrelated workloads.
  • Container startup order matters. Network attachments are not declarative.

User-defined networks solve all three.

Creating a user-defined bridge

docker network create --driver bridge \
  --subnet 10.10.0.0/24 \
  --gateway 10.10.0.1 \
  --opt com.docker.network.bridge.name=br-app \
  app-net

The --driver bridge is explicit; it is the default. The --subnet and --gateway define the addressing; --opt com.docker.network.bridge.name gives the bridge a memorable name (br-app).

docker network ls
docker network inspect app-net

Connecting containers

docker run --network app-net --name web -d nginx
docker run --network app-net --name api -d myapi

The two containers can reach each other by name (web, api). Docker’s embedded DNS resolves the names to the containers’ IPs.

Network scopes

A container can be attached to multiple networks:

docker network connect app-db api
# api is now on both app-net and app-db

This is useful for an API container that needs to talk to both a frontend network and a database network.

Bridge options

docker network create optionsCommon options for production bridge networks
--driver bridge: bridge
--subnet 10.10.0.0/24: CIDR
--gateway 10.10.0.1: IP
--opt com.docker.network.bridge.name=br-app: string
--opt com.docker.network.bridge.enable_icc=true: bool
--opt com.docker.network.bridge.enable_ip_masquerade=true: bool
--opt com.docker.network.bridge.inhibit_ipv4=true: bool
--opt com.docker.network.driver.mtu=1500: int
--opt com.docker.network.container_iface_prefix=eth: string
--ipv6: true
  1. 01--driver bridge= bridge

    The Linux bridge driver.

    Production: Default for Linux. Use macvlan for L2 adjacency.

  2. 02--subnet 10.10.0.0/24= CIDR

    Subnet for the bridge.

    Production: Use private RFC1918 ranges that do not conflict with corporate networks.

  3. 03--gateway 10.10.0.1= IP

    The gateway (host-side bridge IP).

    Production: Pick a stable, predictable address.

  4. 04--opt com.docker.network.bridge.name=br-app= string

    Custom bridge interface name.

    Production: Useful for matching iptables rules and DNS.

  5. 05--opt com.docker.network.bridge.enable_icc=true= bool

    Inter-container communication. Disable for strict isolation.

    Production: Disable by default in multi-tenant environments.

  6. 06--opt com.docker.network.bridge.enable_ip_masquerade=true= bool

    Masquerade outgoing traffic. Required for outbound access.

    Production: Enable by default.

  7. 07--opt com.docker.network.bridge.inhibit_ipv4=true= bool

    Do not assign the gateway IPv4 address to the bridge interface itself.

    Production: Only for setups where an external router owns the gateway address.

  8. 08--opt com.docker.network.driver.mtu=1500= int

    MTU of the bridge. Note the key is driver.mtu, not bridge.mtu — the latter is silently ignored.

    Production: Set 1450 for VXLAN overlays, 9000 for jumbo frames on supported hardware.

  9. 09--opt com.docker.network.container_iface_prefix=eth= string

    Prefix for the interface name inside the container.

    Production: Leave at the default unless a workload hard-codes an interface name.

  10. 10--ipv6= true

    Enable IPv6 on the bridge.

    Production: Pass a second --subnet with an IPv6 CIDR to pin the range: --ipv6 --subnet 2001:db8:1::/64. There is no --subnet6 flag.

DNS resolution

The embedded DNS server runs at 127.0.0.11 inside every container. It resolves:

  • Container names (web, api).
  • Service names in compose (web, api, db).
  • Aliases set with --network-alias.
  • External names via the host’s resolver.

A container on a user-defined network has an /etc/resolv.conf pointing at 127.0.0.11. A container on the default bridge does not: it receives a copy of the host’s resolver configuration instead, with no embedded DNS in the path at all. That single difference is the subject of the next section.

There is no bridge driver option for upstream resolvers. com.docker.network.bridge.dns_servers is not in the driver’s option set and configures nothing. Set resolvers per container with --dns, or host-wide in daemon.json:

docker run --dns 192.0.2.10 --dns-search corp.example.com --network app-net myorg/app:1.0.0
{
  "dns": ["192.0.2.10", "192.0.2.11"],
  "dns-search": ["corp.example.com"]
}

The embedded resolver at 127.0.0.11 still answers container names first and forwards everything else to whichever upstreams you configured.

Read-only / Safeis this container on the embedded resolver?
CONTAINER=migrator
docker inspect --format 'networks: {{range $k, $v := .NetworkSettings.Networks}}{{$k}} {{end}}' "$CONTAINER"
docker exec "$CONTAINER" grep ^nameserver /etc/resolv.conf
networks: bridge
nameserver 192.0.2.10

Illustrative output

networks: bridge with a nameserver that is not 127.0.0.11 is the broken case. The working case looks like this instead:

networks: myproject_default
nameserver 127.0.0.11
Read-only / Safeprove resolution end to end
NETWORK=myproject_default
TARGET=db
docker run --rm --network "$NETWORK" busybox nslookup "$TARGET" 2>&1 | tail -5
Server:		127.0.0.11
Address:	127.0.0.11:53

Name:      db
Address 1: 172.19.0.3 db.myproject_default

Illustrative output

Run the identical command with --network bridge and it returns server can't find db: NXDOMAIN. Two networks, one command, two different answers — that is a verification that can fail, which is what makes it worth running.

MTU

The default MTU is 1500. If you have a tunnel (VXLAN, WireGuard, GRE) between hosts, the effective MTU drops. The standard adjustment:

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

The key is com.docker.network.driver.mtu. The bridge driver’s own options are namespaced com.docker.network.bridge.*name, enable_icc, enable_ip_masquerade, host_binding_ipv4, inhibit_ipv4, gateway_mode_ipv4, gateway_mode_ipv6 — but MTU is not one of them, and com.docker.network.bridge.mtu is a common enough typo to be worth checking for by hand. Do not assume a wrong key would have been rejected loudly; confirm the MTU the kernel actually applied:

Read-only / Safedid the MTU option actually apply?
NETWORK=overlay-net
docker network inspect "$NETWORK" --format '{{json .Options}}'
BR=$(docker network inspect "$NETWORK" --format 'br-{{slice .Id 0 12}}')
ip -o link show "$BR" | grep -o 'mtu [0-9]*'
{"com.docker.network.driver.mtu":"1450"}
mtu 1450

Illustrative output

A mtu 1500 in that second line, against an option that says 1450, is the signature of a mistyped key. The failure mode this prevents is nasty: MTU mismatches do not break connectivity, they break large packets, so the TCP handshake succeeds, small responses work, and only large payloads hang. It presents as “the API works but file uploads time out”.

A smaller MTU is the cost of the tunnel’s overhead. Containers negotiate TCP MSS automatically; UDP applications may need explicit configuration.

Knowledge check

Knowledge check · 6 questions

  1. Q1. Two containers on the same user-defined bridge network communicate by:

  2. Q2. Containers on the default `bridge` network can resolve each other by container name.

  3. Q3. Which command creates a user-defined bridge network with a 24-bit subnet?

  4. Q4. A container on `bridge-A` cannot reach a container on `bridge-B` even though both are on the same host. Why?

  5. Q5. A script runs `docker run --rm myorg/migrator` and the container fails with `could not resolve host: db`. The same image resolves `db` fine under Compose. What is the cause?

  6. Q6. Which network-create option key actually sets the MTU of a Docker bridge network?

Passing score: 75%. Answers are checked in this browser.