Docker DNS failures often produce the same vague errors, even when the causes differ. Changing resolvers may fix one hostname while quietly breaking three internal names.
Split failures into container names, external names, timeouts, and slow replies. Classify the fault first. Then change one layer at a time. Tested with Docker Engine 27.x behavior used by Docker Desktop on macOS.
Prerequisites
You’ll need:
- Docker Engine 27.x or newer, or a current Docker Desktop release
- Permission to run
docker exec,docker run, anddocker network inspect - An existing affected container
- Terminal access to the Docker host
digornslookupin the application container, or permission to launch a temporary diagnostic container- The hostname of a public service, such as
example.com - The expected name of another container, if container-to-container DNS is failing
This workflow matches Docker Engine behavior used by Docker Desktop on macOS. Docker doesn’t run Linux containers natively on iOS. From an iPhone or iPad, use SSH to diagnose the remote Docker host.
Replace these example values throughout:
app: affected containerdb: target containerapp-net: user-defined Docker network192.0.2.53: example DNS server; replace it with a reachable resolver
Quick Diagnosis
1. Classify the failure
Test one expected container name and one external fully qualified domain name (FQDN):
docker exec app getent hosts db
docker exec app getent hosts example.com
A successful pair usually looks like this:
172.20.0.3 db
93.184.216.34 example.com
If getent is missing, try nslookup or dig:
docker exec app nslookup db
docker exec app nslookup example.com
Don’t change DNS settings yet. An immediate NXDOMAIN and a five-second timeout point to different faults. That difference matters more than the application’s error.
| Result | Failure class | Next check |
|---|---|---|
db fails, but example.com works | Container-name resolution | Inspect shared Docker networks |
| Both names fail immediately | Resolver configuration or application behavior | Inspect /etc/resolv.conf and response codes |
| Both names time out | Resolver reachability or firewall | Test UDP and TCP port 53 |
| Lookups succeed after several seconds | Search domains or ndots | Compare short and absolute names |
| Host succeeds, container fails | Different resolver paths | Compare host and container configuration |
2. Inspect the container resolver
docker exec app cat /etc/resolv.conf
A container on a user-defined network commonly shows:
nameserver 127.0.0.11
search corp.example
options ndots:5
127.0.0.11 is Docker’s built-in DNS resolver on port 53. This address is expected. Docker uses it for service discovery on supported networks. It forwards other queries upstream.
Treat the search and options lines as active settings. A long search list with ndots:5 can turn one lookup into several failed queries.

3. Inspect network membership
Show every network attached to each container:
docker inspect --format '{{json .NetworkSettings.Networks}}' app
docker inspect --format '{{json .NetworkSettings.Networks}}' db
Then inspect the network that should contain both containers:
docker network inspect app-net
Look for “Driver”: “bridge”
and both container names under "Containers". If only one appears, Docker DNS can’t publish the other name on that network.

4. Use this decision tree
Can the container resolve an external FQDN?
├── No
│ ├── Lookup fails immediately with NXDOMAIN
│ │ └── Check the queried name, search domains, and upstream resolver
│ └── Lookup times out
│ └── Check resolver reachability plus UDP/TCP port 53
└── Yes
├── Only another container’s name fails
│ └── Confirm both containers share a user-defined network
└── Lookup succeeds but takes several seconds
└── Compare short name vs FQDN; inspect search and ndots
This tree keeps the fault domain small. DNS has enough moving parts already. Testing them all at once mostly creates new symptoms.

Common Issues and Solutions
Problem: A Container Cannot Resolve Another Container by Name
Symptoms:
pingor an application connection works with the target IP addressgetent hosts dbreturns nothingnslookup dbreturnsNXDOMAINor cannot find the name- External names still resolve
- Both containers appear healthy
Cause: Docker’s default bridge network lacks the automatic name resolution available on a user-defined bridge. The same failure occurs when containers use different user-defined networks.
A successful IP connection proves routing works. It doesn’t prove Docker registered the target name on the caller’s network.
Solution:
Create a user-defined bridge:
docker network create --driver bridge app-net
--driver bridge makes the network local to this Docker host. The command returns a network ID:
9d32b90f75d7…
Attach both running containers:
docker network connect app-net app
docker network connect app-net db
docker network connect adds a network without recreating the container. That’s useful during diagnosis. However, manual attachments are easy to lose from your deployment process. Declare the network in Docker Compose for a persistent setup.
Inspect the result:
docker network inspect app-net
For Docker Compose:
services:
app:
image: your-app-image
networks:
- app-net
db:
image: your-database-image
networks:
- app-net
networks:
app-net:
driver: bridge
Apply the Compose configuration:
docker compose up -d
Verification:
docker exec app getent hosts db
Expected output:
172.20.0.3 db
The address may differ. That’s fine. What matters is that db resolves through the shared network instead of a fixed container IP.

Problem: External Hostnames Do Not Resolve
Symptoms:
dbresolves on a shared user-defined networkexample.comfails- The Docker host can resolve the same external name
- Applications report
Temporary failure in name resolution
Cause: The container may use an upstream resolver that Docker’s virtual network can’t reach. Split DNS, virtual private network (VPN) software, and DNS servers bound to host loopback often cause this.
A host resolver such as 127.0.0.1 belongs to the host. A container has its own network namespace. Its loopback address points back to the container.
Solution:
Inspect the current resolver and reproduce the failure:
docker exec app cat /etc/resolv.conf
docker exec app nslookup example.com
If the upstream path is wrong, test a new container with a reachable DNS server:
docker run --rm --dns 192.0.2.53 busybox:1.36 nslookup example.com
--rmdeletes the diagnostic container after it exits.--dnsassigns the resolver to this container only.
Expected output includes an address:
Name: example.com
Address 1: 93.184.216.34
A successful result isolates the fault to the original resolver path. It doesn’t prove that 192.0.2.53 can answer internal zones. Test those zones before making the setting permanent.
For Docker Compose:
services:
app:
image: your-app-image
dns:
- 192.0.2.53
- 192.0.2.54
Recreate the service:
docker compose up -d --force-recreate app
Verification:
docker exec app nslookup example.com
Don’t point production containers at a public resolver because example.com starts working. Public DNS may restore internet lookups but break private records and split-horizon replies. That’s a poor trade for one green test.

Problem: DNS Queries Take Several Seconds
Symptoms:
- Lookups eventually succeed
- Short names are slower than FQDNs
- Application startup pauses during service discovery
/etc/resolv.confcontains several search domains or a highndotsvalue
Cause: A resolver adds search domains to names it treats as relative. With options ndots:5, a name with fewer than five dots may try several suffixes first. Each failed query adds delay.
This can resemble a slow application, especially when several dependencies resolve during startup. Measure DNS before tuning connection pools or retry timers.
Solution:
Inspect the active settings:
docker exec app cat /etc/resolv.conf
Time a short name and its FQDN:
docker exec app sh -c 'time nslookup api'
docker exec app sh -c 'time nslookup api.corp.example.'
The trailing dot marks the second name as absolute. This stops search-domain expansion.
A problematic comparison might show:
api real 0m5.047s
api.corp.example. real 0m0.021s
That’s a 5.026-second penalty for name expansion. It’s long enough to trip plenty of application deadlines.

Reduce unused search domains or set a suitable ndots value in Compose:
services:
app:
image: your-app-image
dns_search:
- corp.example
dns_opt:
- ndots:1
Recreate the service:
docker compose up -d --force-recreate app
Verification:
Repeat both timed queries. A lower ndots value cuts search expansion, but it changes how short internal names resolve. Test every service name the application uses before production rollout. DNS tuning often fixes the measured name and surprises you with an unmeasured one.
Problem: /etc/resolv.conf Contains 127.0.0.11
Symptoms:
- The container lists
nameserver 127.0.0.11 - An operator assumes the loopback address is invalid
- Container or external lookups may still work
Cause: 127.0.0.11:53 is Docker’s built-in resolver. Docker owns this address inside the container’s network namespace. Its presence alone doesn’t identify a fault.
Solution:
Test internal and external DNS paths separately:
docker exec app nslookup db 127.0.0.11
docker exec app nslookup example.com 127.0.0.11
Then inspect network membership:
docker inspect --format '{{json .NetworkSettings.Networks}}' app
If container names fail, fix the shared network. If external names fail, inspect upstream forwarding and firewall access. Replacing 127.0.0.11 without these checks also removes Docker service discovery.
Verification:
A healthy built-in resolver returns an address for a container on the same user-defined network. It should also forward the external query.
Problem: DNS Lookups Time Out
Symptoms:
nslookupprintsconnection timed out; no servers could be reacheddigshowscommunications error- Failure takes several seconds rather than returning immediately
- IP traffic may work on other ports
Cause: A host firewall, upstream firewall, VPN policy, or bad route may block DNS. Most queries use User Datagram Protocol (UDP) port 53. DNS also needs Transmission Control Protocol (TCP) port 53 for large replies, truncated replies, and some operations.
Allowing only UDP can pass basic tests but fail on larger answers. Test both transports from the affected network namespace.
Solution:
Find the resolver address:
docker exec app cat /etc/resolv.conf
If the container shows 127.0.0.11, use the known upstream DNS server for direct reachability tests:
docker run --rm --network container:app nicolaka/netshoot dig @192.0.2.53 example.com
docker run --rm --network container:app nicolaka/netshoot dig +tcp @192.0.2.53 example.com
--network container:appshares the affected container’s network namespace.+tcpforces DNS over TCP instead of the usual UDP query.
Compare the two results:
| UDP test | TCP test | Likely finding |
|---|---|---|
| Works | Works | Port 53 is reachable |
| Fails | Works | UDP 53 is blocked |
| Works | Fails | TCP 53 is blocked; large answers may fail |
| Fails | Fails | Routing, resolver, or firewall failure |
Review host and network firewall rules. Permit outbound UDP and TCP port 53 only to approved resolvers. Don’t flush the whole ruleset to test a theory. On a remote host, that can expose services or end your SSH session before you learn anything useful.
Verification:
Both commands should return a DNS status such as NOERROR without a timeout.

Problem: The Application Image Has No dig or nslookup
Symptoms:
exec: "dig": executable file not found/bin/sh: nslookup: not found- The production image is distroless or intentionally minimal
Cause: Minimal images omit diagnostic packages to reduce image size and attack surface. That’s sensible in production, right up until 02:00 when you want dig.
Solution:
Don’t modify a production image only for troubleshooting. Launch a disposable tool container on the same user-defined network:
docker run --rm --network app-net nicolaka/netshoot dig db
docker run --rm --network app-net nicolaka/netshoot dig example.com
For the closest available network context, share the affected container’s namespace:
docker run --rm --network container:app nicolaka/netshoot dig example.com
A separate container doesn’t share the application’s filesystem, process environment, or resolver library. Sharing its network namespace does reproduce the interfaces and routes. That’s enough for a network-level DNS test.
Netshoot is useful but fairly large compared with BusyBox. Keep it as a temporary diagnostic image. Don’t ship it beside the application.
Verification:
Compare the diagnostic result with the application’s error. If DNS works in the shared namespace, inspect the application’s resolver library, proxy settings, and DNS cache.
Error Messages Table
| Error message | Meaning | First action |
|---|---|---|
Temporary failure in name resolution | Resolver could not complete the lookup, often due to timeout or unavailable upstream DNS | Inspect /etc/resolv.conf; test from the same network |
Name or service not known | The resolver returned no usable record, or the name is malformed | Test the exact FQDN and check search domains |
server can't find db: NXDOMAIN | DNS says the name does not exist in that context | Confirm both containers share a user-defined network |
no servers could be reached | Every configured resolver timed out | Check routing and UDP/TCP port 53 |
communications error to 127.0.0.11#53 | The built-in resolver did not answer that query | Inspect Docker networking and daemon health |
lookup db on 127.0.0.11:53: no such host | Docker DNS answered, but the name is not registered or visible | Inspect shared network membership and aliases |
exec: "dig": executable file not found | The image lacks the diagnostic utility | Use a temporary diagnostic container |
connection refused | A route exists, but nothing accepts DNS at that address and port | Verify the resolver address and service state |
SERVFAIL | The resolver hit an internal failure, often upstream or DNSSEC-related | Query the approved upstream resolver directly |
i/o timeout | The application received no answer before its deadline | Test firewall, packet loss, and resolver latency |
The wording tells you whether a resolver answered. NXDOMAIN and SERVFAIL are replies. A timeout means no usable reply arrived. That distinction saves a fair amount of aimless firewall editing.
Platform-Specific Issues
macOS
Docker Desktop runs Linux containers inside a managed virtual machine. Containers don’t attach directly to the macOS network stack. The host and container can follow different DNS paths.
Start with CLI checks:
docker version
docker info
docker exec app cat /etc/resolv.conf
docker exec app nslookup example.com
Compare the macOS host:
scutil --dns
dig example.com
If DNS fails only while a VPN is connected, test with the VPN connected and disconnected. Corporate DNS software may publish resolvers or routes that Docker Desktop’s virtual network can’t reach. Use the organization’s reachable internal resolver instead of public DNS.
Restart Docker Desktop only after saving the failed resolver settings and test output. A restart may clear a stale route or forwarding state, but it also removes evidence. “A restart fixed it” gets you to lunch. It provides poor fault isolation.

iOS
iOS can’t run Docker Engine or Linux containers natively. Use an iOS SSH client to reach the macOS, Linux, or remote Docker host. Run the same CLI workflow there.
After connecting, confirm the target host before changing anything:
hostname
docker context show
docker ps
Expected output should identify the intended server, Docker context, and affected container. This takes seconds and prevents work on the wrong host. Few things extend an outage more efficiently.
Then run:
docker exec app cat /etc/resolv.conf
docker inspect --format '{{json .NetworkSettings.Networks}}' app
docker exec app nslookup example.com
Don’t use an iOS Safari lookup or phone-level DNS test as proof of container behavior. The iPhone or iPad uses a different network namespace and resolver path. It may also use a different VPN policy.

Configuration Issues
Container-Level DNS
Use --dns when one workload needs a specific resolver:
docker run --detach \
--name app \
--dns 192.0.2.53 \
your-app-image
--detachruns the container in the background.--dnsapplies only to this container.
Docker stores DNS options when it creates the container. Recreate an existing container to apply a changed --dns value.
Container-level settings keep the blast radius small. The cost is repeated configuration when several services need the same resolvers. Undocumented overrides also leave Compose files out of sync.
Daemon-Level DNS
On a Linux Docker host, configure defaults in /etc/docker/daemon.json:
{
"dns": ["192.0.2.53", "192.0.2.54"]
}
Validate the JSON before restarting Docker:
python3 -m json.tool /etc/docker/daemon.json
Valid JSON is printed with indentation. Invalid JSON returns the error’s line and column. That’s better than finding the error through a failed daemon restart.
Warning: Restarting Docker can interrupt running workloads unless live restore and the workload design account for it. Schedule production changes and verify your restart policy first.
Restart Docker on a systemd-based Linux host:
sudo systemctl restart docker
Existing containers may need recreation before they receive the new settings. A daemon-wide resolver is convenient, but one bad address affects every new container on that host.
On Docker Desktop for macOS, use the Docker Engine JSON settings in Docker Desktop. Don’t edit /etc/docker/daemon.json on macOS and expect the managed Linux VM to read it. Preserve existing JSON keys and validate the full object before applying it.

Search Domains and Network Aliases
A Compose configuration can define resolver behavior and stable aliases:
services:
app:
image: your-app-image
networks:
app-net:
dns_search:
- corp.example
dns_opt:
- ndots:1
db:
image: your-database-image
networks:
app-net:
aliases:
- database
networks:
app-net:
driver: bridge
Use aliases when an application expects a name different from the Compose service name. Keep search lists short. Prefer FQDNs for external dependencies where practical.
Aliases apply only to their Docker network. If a container joins two networks, don’t assume an alias on one can resolve from the other.
Getting Help
Collect evidence before opening an issue or escalating to the network team:
docker version
docker info
docker inspect app
docker network ls
docker network inspect app-net
docker exec app cat /etc/resolv.conf
docker exec app nslookup example.com
Remove secrets, environment values, internal domain names, and public IP addresses before sharing output. docker inspect can expose credentials from environment variables. Don’t paste its raw output into a public issue.
Useful log locations and commands include:
- Docker Engine on systemd Linux:
journalctl -u docker - Docker container logs:
docker logs app - Docker Desktop diagnostics: Troubleshoot > Get support
- Application-specific resolver or connection logs
- Firewall logs on the Docker host and upstream gateway
Use the official Docker networking documentation for network behavior. Check the Docker run reference for --dns options. The Docker daemon configuration reference covers daemon settings.
State whether the lookup fails at once, times out, or succeeds slowly. Add the exact queried name, resolver address, and network membership. Those details narrow the fault much faster than a screenshot of an application spinner.
Prevention Tips
- Use user-defined networks: Declare application networks in Compose so container-name resolution stays predictable.
- Use stable service names: Connect to Compose service names or declared aliases, not changing container IP addresses.
- Document approved resolvers: Record which DNS servers are reachable from each Docker network and VPN.
- Test UDP and TCP: Firewall policies should allow both DNS transports to approved resolvers.
- Keep search lists short: Extra suffixes increase query count and make failures slower.
- Set
ndotsdeliberately: Don’t copy Kubernetes-oriented resolver options into Docker without measuring the effect. - Monitor lookup latency: Alert on multi-second DNS resolution before it becomes an application timeout.
- Keep diagnostic tooling separate: Maintain a trusted temporary image such as Netshoot instead of adding tools to production images.
- Capture evidence before restarting: Resolver files, network inspection, and timed queries are more useful than a restart-only fix.
These controls can’t prevent an upstream DNS outage. They can make local failures repeatable. They also stop one resolver change from becoming a host-wide guessing exercise.
Wrapping Up
| Step | Action | Applies To |
|---|---|---|
| 1 | Test a container name and an external FQDN separately | Every DNS failure |
| 2 | Inspect /etc/resolv.conf and Docker networks | Name failures and timeouts |
| 3 | Use a shared user-defined bridge | Container-name failures |
| 4 | Correct upstream DNS or port 53 access | External lookup failures |
| 5 | Reduce search expansion and tune ndots | Slow DNS |
Classify the fault, change one layer, and repeat the same test. Shared-network mistakes cause many container-name failures. Multi-second delays often come from search expansion or blocked retries, rather than a faulty 127.0.0.11 resolver.
Tag the failure as internal, external, immediate, slow, UDP-only, or TCP-only. The repair then becomes ordinary network work. DNS remains complicated, but each test now asks one part a specific question.
Last updated: 2026-08-04 | Applies to Docker CLI workflows accessed from macOS and iOS