Skip to main content
RunBook Academy

Docker & ContainersVII · NetworkingDNS

DNS and the embedded resolver

Intermediate⏱ ~26 mindocker

What you'll learn

  • Explain why the default bridge has no service discovery and a user-defined bridge does
  • Trace a lookup from the application through 127.0.0.11 to the upstream resolver
  • Configure container DNS with the flags that exist, per container and daemon-wide
  • Diagnose a resolution failure without being misled by nslookup

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-12

Not yet marked complete on this device.

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 default bridge network, none of that happens.

The single most common Docker DNS incident

Read-only / Safesame command, two networks
$ # 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
docker run -d --name app \
--network appnet \
--dns 192.0.2.53 \
--dns 198.51.100.53 \
--dns-search example.com \
--dns-opt ndots:1 \
--dns-opt timeout:2 \
--dns-opt attempts:2 \
myapp:1.0.0

In Compose

services:
  app:
    image: myapp:1.0.0
    dns:
      - 192.0.2.53
      - 198.51.100.53
    dns_search:
      - example.com
    dns_opt:
      - timeout:2
      - attempts:2

The keys are dns, dns_search and dns_opt. Note the singular dns_opt, not dns_options.

Daemon-wide

Service impact possibledaemon.json
# /etc/docker/daemon.json
{
"dns": ["192.0.2.53", "198.51.100.53"],
"dns-search": ["example.com"],
"dns-opts": ["timeout:2", "attempts:2"]
}

Diagnosing a resolution failure

Read-only / Safethe four questions
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
  1. 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.
  2. Decide what should resolve internally: container names and network aliases, scoped per network.
  3. Decide what should resolve externally, and point the daemon at real upstream resolvers rather than a loopback stub such as 127.0.0.53.
  4. Test both from inside a container, using getent hosts rather than nslookup, so you exercise the same path the application takes.
  5. 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

  1. Q1. Two containers are started with `docker run` and no `--network` flag. One cannot resolve the other by name. Why?

  2. Q2. You add `--opt com.docker.network.bridge.dns_servers=192.0.2.53` to `docker network create`. What happens?

  3. Q3. Inside a container, `nslookup db` returns NXDOMAIN but the application connects to `db` successfully. What is the explanation?

  4. Q4. Which are real, documented ways to set the DNS servers a container uses? Select all that apply.

  5. 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.