Docker & ContainersXXVI · DNS & Service DiscoveryEmbedded DNS
Embedded DNS — how service name resolution works
What you'll learn
- Describe what the embedded resolver answers from and what it forwards
- Explain why resolution is scoped per network, not per host
- Predict the behaviour when several containers share one name
- Recognise that the resolver has no health awareness
- Diagnose a lookup failure by asking the resolver directly
Prerequisites
None — start here.
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-12
On a user-defined network, Docker runs an embedded DNS server at
127.0.0.11 inside each container’s network namespace. Container names and
network aliases resolve to addresses on that network. On the default bridge
network there is no embedded resolver and no service discovery at all.
The important thing about the embedded resolver is what it resolves from. It has no zone file and no cache of its own. Every answer comes from the daemon’s live model of which containers are attached to which networks under which names — which is why the answers are always current, and why “current” is not the same as “working”.
The lookup path
sequenceDiagram
participant App as Application
participant DNS as Embedded DNS<br/>127.0.0.11
participant Reg as Daemon network registry
participant Up as Upstream resolvers
App->>DNS: A? api
DNS->>Reg: is "api" a name or alias<br/>on a network this container is on?
alt known
Reg-->>DNS: 172.18.0.5
DNS-->>App: 172.18.0.5
else not known
DNS->>Up: forward, servers tried in order
Up-->>DNS: answer, or NXDOMAIN
DNS-->>App: same
end
Two rules govern the first branch, and both are about scope:
- The registry is consulted only for networks this container is attached
to. A container on
frontendcannot resolve a container that exists only onbackend, even though both are on the same host and the daemon knows about both. - A container attached to several networks can resolve names on all of them. It is the union, not an intersection, and it is why a shared “management” network makes previously invisible names resolvable everywhere.
Forwarding, the second branch, is a plain recursive-resolver relationship:
the embedded server “queries upstream servers in order and stops after a
successful response or an NXDOMAIN response”.
Asking the resolver directly
CONTAINER=app
# What does the resolver itself say?
docker exec "$CONTAINER" nslookup api 127.0.0.11
# The path the application actually takes: /etc/hosts, then NSS, then DNS
docker exec "$CONTAINER" getent hosts api
# If the image is minimal, borrow a toolbox in the same namespace
docker run --rm -it --network "container:$CONTAINER" \
nicolaka/netshoot dig @127.0.0.11 apiThe last form is the one to remember. --network container:$CONTAINER puts
the debug container in the same network namespace as the target, so
127.0.0.11 is the same resolver, on the same networks, with the same view.
Running dig from a different container answers a different question.
Several containers, one name
Scale a service and more than one container carries the same alias. The resolver returns all of the matching addresses, and clients that use only the first get an even-ish spread as the order varies between answers.
$ docker compose up -d --scale worker=3
docker compose exec app getent hosts worker
echo '--- again ---'
docker compose exec app getent hosts worker172.18.0.4 worker
172.18.0.5 worker
172.18.0.6 worker
--- again ---
172.18.0.5 worker
172.18.0.6 worker
172.18.0.4 workerIllustrative output
Custom DNS servers
Setting dns: on a service replaces the upstream servers the embedded
resolver forwards to. It does not replace the embedded resolver itself, and
it does not disable service discovery.
services:
app:
image: myapp:1.0.0
networks: [appnet]
dns:
- 192.0.2.53
- 198.51.100.53
dns_search:
- example.com
- internal.example.com
The container’s /etc/resolv.conf still reads nameserver 127.0.0.11.
Container names still resolve from the registry. Only the forwarding target
changed. An application configured to talk to 8.8.8.8 directly, bypassing
resolv.conf entirely, is the case where service names genuinely stop
working — and the fix is to make the application use the system resolver, not
to change anything in Docker.
Sanity check
docker exec "$CONTAINER" cat /etc/resolv.confshowsnameserver 127.0.0.11.getent hostsfrom inside the container resolves both a peer service name and an external name.- For any name backed by more than one container, something with health checks sits in front of it — you can name what that something is.
- No two services in the project share a name, even across separate networks.
Knowledge check
Knowledge check · 5 questions
Q1. Embedded DNS resolves service names only for containers on:
Q2. Three containers share the alias `worker`. One is running but wedged and answers nothing. What does the embedded resolver return?
Q3. A container is on the `frontend` network. Another container exists on `backend` only. The first cannot resolve the second. What is happening?
Q4. Setting `dns: [192.0.2.53]` on a Compose service changes which of the following? Select all that apply.
Q5. After a container restarts with a new address, peers must wait for a DNS TTL before they resolve it correctly.
Passing score: 75%. Answers are checked in this browser.