Docker DNS Resolution Not Working: Fixes

The Problem

Containers can’t resolve domain names. You see errors like:

Could not resolve host: github.com
dial tcp: lookup registry-1.docker.io: no such host
getaddrinfo ENOTFOUND api.example.com
Name or service not known
Temporary failure in name resolution

This happens during docker build (can’t pull packages), at container startup (can’t reach databases by hostname), or at runtime (API calls fail).

The Cause

Docker containers use a built-in DNS server at 127.0.0.11 for resolving container names on custom networks. For external domains (anything not a container name), Docker falls back to the DNS servers configured on the host — typically from /etc/resolv.conf. The most common failures:

CauseSymptomFrequency
systemd-resolved conflictHost /etc/resolv.conf points to 127.0.0.53 — unreachable from containersVery common (Ubuntu/Debian default)
Docker daemon DNS misconfiguredNo valid DNS in Docker configCommon
Container on default bridge networkNo Docker DNS, relies only on host DNSCommon
Firewall blocking outbound DNSDNS packets dropped by UFW/iptablesOccasional
Alpine musl resolver limitationsIPv6 AAAA lookups fail or timeoutOccasional

The Fix

Method 1: Fix systemd-resolved Conflict (Most Common)

On Ubuntu 22.04+, /etc/resolv.conf is a symlink to ../run/systemd/resolve/stub-resolv.conf, which points DNS at 127.0.0.53. This works for the host but not for Docker containers (they can’t reach 127.0.0.53 on the host’s loopback).

Option A: Point Docker at real DNS servers

Create or edit /etc/docker/daemon.json:

{
  "dns": ["1.1.1.1", "9.9.9.9"]
}

Restart Docker:

sudo systemctl restart docker

This tells all containers to use Cloudflare and Quad9 instead of the host’s resolv.conf.

Option B: Fix resolv.conf to use the real resolver

sudo rm /etc/resolv.conf
sudo ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf

This symlinks to systemd-resolved’s upstream resolvers (the real DNS servers), not the stub listener. Containers inherit these.

Option C: Replace resolv.conf entirely

sudo rm /etc/resolv.conf
echo -e "nameserver 1.1.1.1\nnameserver 9.9.9.9" | sudo tee /etc/resolv.conf
sudo chattr +i /etc/resolv.conf  # Prevent systemd from overwriting

Method 2: Per-Container DNS Configuration

Override DNS for a specific container in docker-compose.yml:

services:
  myapp:
    image: myapp:latest
    dns:
      - 1.1.1.1
      - 9.9.9.9

Or for a local DNS server (like Pi-hole):

services:
  myapp:
    image: myapp:latest
    dns:
      - 192.168.1.10

Method 3: Fix Docker Network DNS

Containers on Docker’s default bridge network don’t get Docker’s built-in DNS (127.0.0.11). Only containers on custom/user-defined networks get Docker DNS for container name resolution.

If containers can’t resolve each other by name:

services:
  app:
    networks:
      - mynet
  db:
    networks:
      - mynet

networks:
  mynet:
    driver: bridge

Now app can reach db by hostname.

Method 4: Fix Firewall Rules

If UFW or iptables blocks outbound DNS:

# Allow outbound DNS
sudo ufw allow out 53/tcp
sudo ufw allow out 53/udp

# Allow Docker bridge traffic
sudo ufw allow in on docker0

Check if DNS packets are reaching the internet:

docker run --rm alpine nslookup google.com 1.1.1.1

If this works but normal resolution doesn’t, the issue is in Docker’s DNS configuration, not the firewall.

Method 5: Fix Alpine DNS Issues

Alpine Linux uses musl libc, which handles DNS differently from glibc. Common issue: IPv6 AAAA lookups timeout before falling back to IPv4 A lookups, causing 5-second delays.

Add to your Dockerfile or container:

RUN echo "options single-request-reopen" >> /etc/resolv.conf

Or in Docker Compose:

services:
  myapp:
    dns_opt:
      - single-request-reopen

Prevention

1. Always Use Custom Networks

Define networks in your Docker Compose files. Never rely on the default bridge for DNS resolution between containers.

2. Set DNS in daemon.json

Configure Docker-wide DNS once:

{
  "dns": ["1.1.1.1", "9.9.9.9"]
}

3. Test DNS Early

Add a DNS test to your container startup or health check:

healthcheck:
  test: ["CMD-SHELL", "nslookup example.com || exit 1"]
  interval: 30s
  timeout: 5s

4. Use IP Addresses for Critical Dependencies

For database connections in the same Compose stack, use the Docker service name (which resolves via Docker DNS). For external services, consider using IPs if DNS instability is a concern.

Comments