Network Drivers in Depth: bridge, host, none and Container DNS

Intermediate
12 min

Network Drivers in Depth: bridge, host, none and Container DNS

The networking lesson introduced the idea that containers on the same user-defined network can reach each other by name. This lesson goes underneath that: what each network driver actually does, how Docker's embedded DNS resolves names and aliases, how to publish ports safely, and how to isolate services so a compromised front end cannot talk directly to the database.

The Drivers

| Driver | What it does | Typical use | |--------|--------------|-------------| | bridge | Private virtual switch on the host; containers get their own IPs and NAT to the outside | Default for single-host apps | | host | No isolation; the container shares the host's network stack and ports | High-performance or port-heavy tools on Linux | | none | Only a loopback interface; no external connectivity | Batch jobs that must not reach the network | | overlay | Spans multiple hosts | Swarm services | | macvlan | Gives the container a MAC and IP on the physical LAN | Legacy apps that need to appear as real hosts |

bash
docker network ls docker run --rm --network host nginx:alpine # listens directly on host port 80 docker run --rm --network none alpine ping -c1 1.1.1.1 # Network unreachable

host networking is Linux-native; Docker Desktop offers it as an opt-in setting. -p is ignored with --network host because there is nothing to map.

Default Bridge vs User-Defined Bridge

Every daemon has a network named bridge (interface docker0) that containers join when you pass no --network. It is deliberately limited: containers can reach each other only by IP address, and the legacy --link flag is the only way to add names. A user-defined bridge adds:

  • Automatic DNS: every container is resolvable by name and by any --network-alias.
  • Isolation: only containers on that network can reach each other.
  • Hot attach and detach with docker network connect and disconnect.
  • Custom subnets and gateways.
bash
docker network create --driver bridge --subnet 10.10.0.0/24 app-net docker run -d --name redis --network app-net redis:7-alpine docker run --rm --network app-net redis:7-alpine redis-cli -h redis ping # PONG

Compose creates one user-defined network per project automatically, which is why service names just work there.

How Container DNS Works

Inside a container on a user-defined network, /etc/resolv.conf points to 127.0.0.11, Docker's embedded DNS server. It answers for container names, network aliases and Compose service names, and forwards everything else to the host's resolvers. Several containers can share one alias, in which case the resolver returns all their IPs, a crude form of load balancing.

bash
docker run -d --network app-net --network-alias cache redis:7-alpine docker run -d --network app-net --network-alias cache redis:7-alpine docker run --rm --network app-net alpine nslookup cache # two addresses

Names are resolvable only within a shared network. A container attached to two networks can act as a bridge between them, which is the pattern used to expose an API to a proxy while keeping the database private.

Reaching the Host and the Internet

Outbound traffic from a bridge network is NATed through the host, so containers can reach the internet without any flags. Reaching a service on the host (a local database, for example) uses the special name host.docker.internal, which Docker Desktop provides automatically. On Linux, add it explicitly:

bash
docker run --rm --add-host=host.docker.internal:host-gateway alpine \ wget -qO- http://host.docker.internal:8000/

A network created with --internal has no route to the outside at all, which is ideal for databases and queues.

Publishing Ports Safely

-p maps a host port to a container port and, by default, binds to all host interfaces. That exposes the service to the network the host is on, bypassing many host firewalls because Docker manipulates iptables directly.

bash
docker run -d -p 8080:80 nginx:alpine # 0.0.0.0:8080 -> 80: reachable from LAN docker run -d -p 127.0.0.1:8080:80 nginx:alpine # loopback only docker run -d -P my-api:1.0 # random host ports for each EXPOSE docker port <container>

Publish only the entry point (a reverse proxy or the API), bind development ports to 127.0.0.1, and leave databases unpublished; other containers reach them over the internal network without any host port.

Common Mistakes

  • Using the default bridge network and wondering why ping db fails; create a user-defined network.
  • Expecting localhost inside a container to mean the host; it means the container itself.
Quick Quiz
Question 1 of 3

Why can containers on a user-defined bridge reach each other by name, while those on the default bridge cannot?

Key Takeaways

  • bridge is the default and NATs outbound traffic; host removes isolation; none removes connectivity.
  • User-defined bridges add DNS by name and alias, isolation and hot attach; the default bridge does not.
  • Docker's embedded DNS at 127.0.0.11 resolves names only within a shared network; multi-network containers bridge tiers.
  • host.docker.internal reaches the host; --internal networks have no external route.
  • Publish only entry points, bind development ports to 127.0.0.1, and never publish databases.

Next lesson: Environment Variables and Configuration — pass settings into containers with -e, env files and Compose.

Network Drivers in Depth: bridge, host, none and Container DNS - Docker | CodeYourCraft | CodeYourCraft