How Docker's Embedded DNS Resolves Container Names
Docker containers resolve each other by name via an embedded DNS server at 127.0.0.11. Learn how it works, why it beats /etc/hosts hacks, and how to debug name resolution.
Published on • October 28, 2026
AI Assistant

docker compose up, and something quietly remarkable happens: your api container connects to db by typing db:5432. No IPs, no config files, no restarts when a container is recreated with a new address. Given that Docker containers get fresh, ephemeral IPs every time they’re created, this feels like sorcery. It isn’t — it’s an embedded DNS server, and understanding how it works explains several behaviors that otherwise look like bugs.
The magic number: 127.0.0.11
Inside every container on a user-defined network, Docker sets the container’s resolv.conf to point at 127.0.0.11 — a DNS server that doesn’t exist on your host, but inside the container’s network namespace. It’s provided by the Docker daemon (libnetwork), reachable only from within the container, and it’s the reason container-to-container name resolution works at all.
The resolution flow when api looks up db:
- The app calls
getaddrinfo("db", 5432)as usual. - The resolver reads
/etc/resolv.conf, seesnameserver 127.0.0.11, and sends the DNS query there. - The embedded DNS server recognizes
dbas a container name on the same user-defined network and returns the container’s current IP on that network. - If the name isn’t a container, the query is forwarded upstream to the host’s original resolvers — so
google.comstill works from inside the container.
The critical design point: containers on the same user-defined network see each other’s names. This is why Compose networks give you name-based discovery for free, and why two containers on the default bridge network (which does not use the embedded DNS for name resolution) mysteriously can’t ping each other by name.
Why not just use /etc/hosts?
Earlier Docker versions injected container names into each container’s /etc/hosts. The embedded DNS replaced that for good reasons:
- Freshness. With DNS, recreating
dbupdates the name→IP mapping instantly for every peer. With static hosts files, every other container would need rewriting (and restarting) on every event. - Scale. A network with 50 containers doesn’t need 50 hosts entries shuffled around; it needs one authoritative source.
- Structure. DNS lets you layer semantics — the embedded server resolves container names first, then forwards everything else upstream, so applications need zero special configuration.
You can still inspect the wiring yourself:
docker exec -it api cat /etc/resolv.conf # nameserver 127.0.0.11
docker exec -it api getent hosts db # resolve a peer by name
docker network inspect mynet # see each container's DNS name and IP
Useful behaviors to know
Aliases matter more than names. When a container joins a network, it has its Compose service name plus any explicit aliases you attach. Aliases are how one container can answer under multiple names — useful when you want a stable hostname (cache) independent of which replica of a scaled service answers.
Search domains make short names work. Docker adds a search line to resolv.conf matching the network’s scope, so bare names resolve without qualification. In multi-network setups this is often why a name you swore was unreachable suddenly resolves — you’re seeing search-path resolution, not magic.
Cross-network calls need the network-scoped alias or IP. A container attached to two networks can reach peers on either, but resolution behavior depends on which network the name lives on. When something resolves to a surprising IP, docker network inspect is the ground truth.
External names are just forwarded queries. If you use Compose’s external_links or attach to an external network, resolution of those names goes through the same embedded server — it knows the network’s full membership, not just your project’s.
Debugging name resolution failures
When “it can’t resolve the hostname” bites, run the checks in order:
- Are both containers on the same user-defined network?
docker network inspect <net>and look at theContainerssection. Default bridge = no name resolution. - Is it DNS or is it the target?
docker exec ... nslookup dbtests resolution separately from connectivity. Resolution working + connection refused means the app isn’t listening where you think. - Check the resolver config. If
resolv.confdoesn’t show127.0.0.11, the container is on the default bridge or something overrode DNS settings. - Mind the search path. A name resolving to an unexpected address usually means search-domain expansion hit a different suffix first.
The bottom line
Docker’s name resolution is one design decision done right: a per-namespace DNS server at 127.0.0.11 that answers for network-local names and forwards everything else upstream. Containers get stable, configuration-free discovery while keeping fresh, disposable IPs. Once you know the embedded DNS is there — and that “same user-defined network” is its boundary — container networking’s strangest behaviors become predictable, debuggable, and honestly a little boring. That’s the goal.