Resolving DNS Resolution Failures Between Local Containers
A service cannot resolve another by name in Compose. Fix embedded DNS at 127.0.0.11, shared networks, network aliases, and depends_on ordering for reliable lookups.
Your app container fails to reach db with getaddrinfo ENOTFOUND db or could not translate host name "db", even though both are defined in the same Compose file — a recurring local failure point during onboarding.
Diagnostic
From inside the failing container, try to resolve the peer service name and inspect which networks each container actually joined.
#!/usr/bin/env bash
set -euo pipefail
docker compose exec app getent hosts db || echo "resolution failed"
docker compose exec app cat /etc/resolv.conf
docker inspect -f '{{.Name}} -> {{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}' \
"$(docker compose ps -q app)" "$(docker compose ps -q db)"
Expected BAD output — the name does not resolve and the two containers sit on different networks:
resolution failed
nameserver 127.0.0.11
options ndots:0
/app-app-1 -> app_frontend
/app-db-1 -> app_backend
The nameserver 127.0.0.11 line is correct — that is Docker's embedded DNS. The real fault is that app and db are on different user-defined networks, so the embedded resolver has no record for db in app's scope.
Root cause
Docker's embedded DNS server runs at 127.0.0.11 inside every container on a user-defined network and resolves service names to container IPs — but only for containers attached to the same network. Service-name DNS silently fails when the two services are placed on different networks (often by per-service networks: lists that do not overlap), when a service is reached before it has registered (a timing gap that depends_on alone does not close, because it waits for start, not readiness), or when code targets the wrong name — Compose registers both the service key and the container name, and custom aliases add more. The embedded resolver is working; it just has nothing to answer with. This is the local-container view of the routing covered in configuring local DNS for microservice routing.
Resolution
- Put both services on a shared user-defined network so the embedded DNS scope overlaps.
- Add explicit
aliasesif code references a hostname other than the service key. - Gate the dependent on readiness with
depends_on: condition: service_healthy, not baredepends_on. - Recreate the stack so network membership and DNS records are rebuilt.
# docker-compose.yml
services:
app:
build: .
depends_on:
db:
condition: service_healthy
networks:
- appnet
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: localdev
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 2s
timeout: 3s
retries: 10
networks:
appnet:
aliases:
- database # app can use db OR database
networks:
appnet:
driver: bridge
Then rebuild membership and confirm the resolver answers:
#!/usr/bin/env bash
set -euo pipefail
docker compose down
docker compose up -d --wait
docker compose exec app getent hosts db
Expected output
The peer name now resolves to the embedded DNS record (an IP on the shared bridge), and both containers list the same network:
$ docker compose exec app getent hosts db
172.20.0.3 db
$ docker inspect -f '{{.Name}} -> {{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}' app-app-1 app-db-1
/app-app-1 -> appnet
/app-db-1 -> appnet
Prevention
- Default to a single shared network and only segment when isolation is a real requirement — document the topology alongside mapping microservice dependencies for local dev.
- Always pair cross-service calls with
depends_on: condition: service_healthyso the name exists and the service is ready — see resolving service startup-order and healthcheck races. - Add a
getent hosts <peer>probe to your onboarding health-check script.
macOS / Windows (Docker Desktop): the embedded resolver lives inside the Linux VM; you cannot resolve service names from the host. Use
localhost:<published-port>from the host and service names only between containers. WSL2: a stale/etc/resolv.conf(fromgenerateResolvConf=false) can shadow127.0.0.11; let Docker manage container resolv.conf and avoid injecting host DNS into containers. Apple Silicon (ARM64): no DNS behavior difference, but a service that exits on startup never registers a DNS record — confirm the peer is actually running first.
Rollback
#!/usr/bin/env bash
set -euo pipefail
git checkout -- docker-compose.yml && docker compose up -d --force-recreate # restore prior networks