Container DNS is not host DNS. For containers attached to a custom
network, Docker runs an embedded DNS server at 127.0.0.11 inside the
container’s network namespace. It resolves container names and network
aliases from the daemon’s own registry, and forwards everything else to the
resolvers the host uses.
For containers on the defaultbridge network, none of that happens.
The single most common Docker DNS incident
Read-only / Safesame command, two networks— Identical command. The only difference is which network the containers are on.
$ # Default bridge — no --network flag
docker run -d --name db-a alpine:3.20 sleep 600
docker run --rm --name app-a alpine:3.20 getent hosts db-a
echo '=== user-defined network ==='
docker network create appnet
docker run -d --name db-b --network appnet alpine:3.20 sleep 600
docker run --rm --network appnet alpine:3.20 getent hosts db-b
(no output, exit status 2)
=== user-defined network ===
172.18.0.2 db-b
Illustrative output
What the embedded resolver does with a query
sequenceDiagram participant App as Application participant Res as libc resolver participant DNS as Embedded DNS<br/>127.0.0.11 participant Up as Upstream<br/>(host resolv.conf) App->>Res: getaddrinfo("db") Res->>Res: check /etc/hosts first Res->>DNS: A? db alt db is a container or alias on one of this container's networks DNS-->>Res: 172.18.0.2 else not known to the daemon DNS->>Up: forward the query, servers tried in order Up-->>DNS: answer, or NXDOMAIN DNS-->>Res: same end Res-->>App: address, or EAI_NONAME
Two details in that diagram earn their place:
/etc/hosts is consulted before DNS, per nsswitch.conf. An entry
there — including one you added with --add-host — wins over anything the
embedded resolver would have said, and no DNS debugging will show it.
Upstream servers are tried in order, and the embedded server “stops
after a successful response or an NXDOMAIN response”. A first resolver
that answers NXDOMAIN authoritatively ends the search; the second one is
never asked. That is standard resolver behaviour and it surprises people
who list a public resolver second as a “backup”.
Configuring DNS
There are exactly three places to set this, and it is worth being precise
about them because invented options are common in this area.
Per container
Configuration changerun flags— --dns replaces the upstream servers the embedded resolver forwards to. It does not replace 127.0.0.11 in resolv.conf on a custom network.
Read-only / Safethe four questions— In order. Each one eliminates a layer; do not skip to the last.
CONTAINER=app
# 1. Which network is it on? Anything showing "bridge" has no service discovery.
docker inspect --format \
'{{range $n, $c := .NetworkSettings.Networks}}{{$n}} {{end}}' "$CONTAINER"
# 2. What does the resolver config say?
docker exec "$CONTAINER" cat /etc/resolv.conf
# 3. Is there a static entry shadowing DNS entirely?
docker exec "$CONTAINER" cat /etc/hosts
# 4. Does resolution work, using the same path the application uses?
docker exec "$CONTAINER" getent hosts db
docker exec "$CONTAINER" getent hosts example.com
Put every service on a user-defined network. In Compose this is automatic; with docker run it is the --network flag. This alone resolves most name-resolution reports.
Decide what should resolve internally: container names and network aliases, scoped per network.
Decide what should resolve externally, and point the daemon at real upstream resolvers rather than a loopback stub such as 127.0.0.53.
Test both from inside a container, using getent hosts rather than nslookup, so you exercise the same path the application takes.
Give critical services a health check that exercises resolution, so a DNS failure is reported as unhealthy rather than as a pile of application errors.
Sanity check
No production container is on the default bridge network.
docker exec "$CONTAINER" cat /etc/resolv.conf shows nameserver 127.0.0.11 for every container you expect service discovery on.
getent hosts <peer-name> from inside a container returns an address.
getent hosts example.com from inside the same container also returns one,
proving forwarding works and not just the registry.
Knowledge check
Knowledge check · 5 questions
Q1. Two containers are started with `docker run` and no `--network` flag. One cannot resolve the other by name. Why?
Q2. You add `--opt com.docker.network.bridge.dns_servers=192.0.2.53` to `docker network create`. What happens?
Q3. Inside a container, `nslookup db` returns NXDOMAIN but the application connects to `db` successfully. What is the explanation?
Q4. Which are real, documented ways to set the DNS servers a container uses? Select all that apply.
Q5. When a container on a user-defined network restarts with a new IP address, other containers must wait for a DNS TTL to expire before they resolve the new address.
Passing score: 75%. Answers are checked in this browser.