Custom DNS in Docker: Practical Engineering Control Over Container Name Resolution
Developers often encounter erratic connectivity between Docker containers—especially in multi-tier or service-mesh setups where internal hostnames must resolve reliably. The culprit is frequently Docker’s default DNS configuration, which simply injects the host’s /etc/resolv.conf
into each container. This works until it doesn’t.
Consider a deployment where a microservice references api.internal.local
. If the host’s DNS can’t resolve this, containers will fail silently, generating logs like:
curl: (6) Could not resolve host: api.internal.local
Rather than chasing intermittent failures, take explicit control.
DNS Behavior in Docker: What Actually Happens?
- On Linux, Docker Engine (tested with 20.10.x–24.0.x) defaults to copying
/etc/resolv.conf
into the container at runtime. - Containers join a user-defined bridge or overlay network; DNS queries typically route via a Docker internal DNS server (typically
127.0.0.11
for bridge networks). - This internal DNS is fast but can’t resolve anything outside the upstream nameservers it’s given.
Edge case: With Mac/Windows Docker Desktop, networking is virtualized—DNS behavior can vary; always inspect /etc/resolv.conf
before assuming.
Injecting Custom DNS: Approaches Compared
1. Per-Container DNS Overrides (docker run --dns
)
Force a container to use known, reliable DNS servers:
docker run --dns 10.100.10.53 --dns 8.8.4.4 --rm -it ubuntu:22.04 bash
Check the result:
cat /etc/resolv.conf
Sample output:
nameserver 10.100.10.53
nameserver 8.8.4.4
Note: If the upstream DNS is unreachable within the container’s virtual network, resolution will still fail.
2. Specify Search Domains (--dns-search
)
For environments relying on unqualified hostnames or split DNS zones:
docker run --dns-search corp.acme.net -it --rm ubuntu:22.04 bash
This appends corp.acme.net
to unqualified lookups, meaning ping db01
attempts db01.corp.acme.net
.
Combine both flags when required:
docker run --dns 172.30.0.2 --dns-search dev.internal --rm -it debian:12 bash
3. Docker Compose Integration
Compose files formalize configuration for CI/CD pipelines—no room for mistakes or implicit defaults.
Example docker-compose.yml
:
version: "3.8"
services:
backend:
image: registry.local/backend:2.12
dns:
- 10.1.2.3
- 1.1.1.1
dns_search:
- devteam.local
Deploy:
docker compose up -d
Gotcha: Each time you update your compose file, running docker compose down && docker compose up -d
ensures all containers inherit the new DNS config (restart alone won’t always reload /etc/resolv.conf
).
Global Docker Daemon DNS
Some environments require every container—legacy and new—to use a specific set of resolvers. Update /etc/docker/daemon.json
:
{
"dns": ["172.20.30.40", "8.8.8.8"]
}
Apply changes:
sudo systemctl restart docker
Known issue: Daemon restarts will disrupt existing containers, albeit briefly. Batch restarts during maintenance.
Validation and Troubleshooting
Troubleshoot before assuming correctness:
-
Shell Into Running Container:
docker exec -it app_container bash
-
Examine
/etc/resolv.conf
:
Check nameserver entries precisely match configuration. -
Test DNS Resolution:
apt update && apt install -y dnsutils dig backend1.devteam.local @10.1.2.3 ping google.com
-
If resolution fails:
- Confirm container network can access specified nameservers (
nmap -p 53 10.1.2.3
). - Verify that security groups, Docker bridge/overlay settings, and upstream DNS ACLs are not blocking traffic.
- Confirm container network can access specified nameservers (
Side note: For complex Kubernetes workloads, consider CoreDNS customization instead; Docker DNS settings won’t always propagate through orchestration layers.
Tip: Using Environment Variables For Dynamic DNS (Non-Obvious)
For advanced cases—such as ephemeral CI runners—you can interpolate DNS servers via environment variables and a small entrypoint script to write /etc/resolv.conf
before starting the main process. This approach bypasses Docker’s own config but gives bleeder-edge flexibility (with risk).
Final Considerations
DNS misconfiguration is rarely obvious but often catastrophic, especially with containers distributed across environments—cloud, local VMs, hybrid. Explicitly set DNS at the right level (container, compose, or daemon) after careful inventory of network topology.
Alternate approach: some teams run in-container dnsmasq forwarding outbound requests, but that’s another set of tradeoffs—monitor resolver logs for blind spots.
No universal best answer exists, but defaulting to Docker’s out-of-the-box DNS is rarely sufficient for production-grade deployments. Take control, verify in the shell, and document any DNS layer outside Docker that could intercept queries.
Quick Reference Table
Method | Scope | Best For | Command/Config |
---|---|---|---|
--dns , --dns-search | Single container | One-off troubleshooting | docker run ... |
dns , dns_search in YML | Application | Multi-container stacks | docker compose ... |
Daemon JSON | Host-wide | Enforced org-wide policy | /etc/docker/daemon.json |
When a container's network misbehaves, DNS is often the first layer worth auditing—before blaming application code or cloud providers.