How to Configure Custom DNS in Docker Containers: Overriding /etc/hosts with dnsmasq


2 views

When running applications in Docker containers, you might need custom hostname resolutions that aren't possible through the default /etc/hosts file (which is read-only in containers). The standard approach of modifying /etc/hosts directly won't work, as Docker overwrites this file during container initialization.

dnsmasq provides a lightweight DNS server that can be configured to:

  • Serve custom hostname resolutions
  • Cache DNS queries
  • Integrate with Docker's networking

Here's how to properly set up dnsmasq in your Docker container:

FROM ubuntu:22.04

# Install dnsmasq and configure it
RUN apt-get update && apt-get install -y dnsmasq

# Main configuration
RUN echo 'listen-address=127.0.0.1' >> /etc/dnsmasq.conf
RUN echo 'resolv-file=/etc/resolv.dnsmasq.conf' >> /etc/dnsmasq.conf
RUN echo 'conf-dir=/etc/dnsmasq.d,.conf' >> /etc/dnsmasq.conf
RUN echo 'user=root' >> /etc/dnsmasq.conf

# Upstream DNS servers
RUN echo 'nameserver 8.8.8.8' > /etc/resolv.dnsmasq.conf
RUN echo 'nameserver 8.8.4.4' >> /etc/resolv.dnsmasq.conf

# Custom host entries
RUN mkdir -p /etc/dnsmasq.d
RUN echo 'address=/mydomain.com/127.0.6.1' > /etc/dnsmasq.d/custom_hosts.conf

# Start dnsmasq in the background
CMD ["sh", "-c", "dnsmasq && your_application_command"]

The key is to properly configure Docker's DNS settings. Here are the correct approaches:

# Method 1: Using --dns flag
docker run --dns=127.0.0.1 my/container

# Method 2: Modifying Docker daemon settings (persistent)
# Add to /etc/docker/daemon.json
{
  "dns": ["127.0.0.1"]
}

To verify your configuration is working:

# Inside the container:
dig mydomain.com @127.0.0.1
nslookup mydomain.com 127.0.0.1

For more complex setups, consider:

  • Using Docker's --network host mode for simpler networking
  • Creating a custom bridge network with specific DNS settings
  • Using Docker Compose with explicit DNS configuration
# docker-compose.yml example
version: '3'
services:
  app:
    build: .
    dns:
      - 127.0.0.1
      - 8.8.8.8
    networks:
      - mynet

networks:
  mynet:
    driver: bridge

When using dnsmasq in containers:

  • Enable DNS caching in dnsmasq for better performance
  • Consider running dnsmasq in a separate container for multi-container setups
  • Monitor DNS resolution times to identify bottlenecks

When working with Docker containers, you'll quickly discover that modifying /etc/hosts directly isn't possible as the file is read-only. This becomes problematic when you need custom hostname resolutions for:

  • Local development environments
  • Service discovery between containers
  • Testing DNS configurations
  • Overriding production endpoints

The approach using dnsmasq in your Dockerfile has several fundamental issues:

# Problem 1: The service won't persist after container start
RUN service dnsmasq start  # This only runs during build phase

# Problem 2: Docker's internal DNS takes precedence
# Even with --dns flag, Docker manages /etc/resolv.conf

Solution 1: Using Docker's --add-host Flag

The simplest approach for static host mappings:

docker run --add-host="mydomain:127.0.6.1" your_image

For multiple entries:

docker run \
  --add-host="mydomain:127.0.6.1" \
  --add-host="api.mydomain:192.168.1.100" \
  your_image

Solution 2: Custom DNS with docker-compose

For more complex setups using docker-compose.yml:

version: '3'
services:
  app:
    image: your_image
    dns: 127.0.0.1
    extra_hosts:
      - "mydomain:127.0.6.1"
      - "api.mydomain:192.168.1.100"
  dnsmasq:
    image: andyshinn/dnsmasq
    ports:
      - "53:53/tcp"
      - "53:53/udp"
    command: -A "/mydomain/127.0.6.1" --log-queries

Solution 3: Permanent DNS Configuration

For persistent DNS configuration across all containers:

# Create or edit /etc/docker/daemon.json
{
  "dns": ["192.168.1.1", "8.8.8.8"],
  "dns-search": ["mydomain.com"],
  "dns-opts": ["timeout:2", "attempts:2"]
}

Then restart Docker daemon:

sudo systemctl restart docker

When debugging DNS issues:

# Check container's DNS resolution
docker exec -it container_name cat /etc/resolv.conf

# Test DNS resolution inside container
docker exec -it container_name nslookup mydomain

# Verify Docker daemon DNS settings
docker info | grep -i dns

For complex DNS requirements, consider using CoreDNS:

FROM coredns/coredns
COPY Corefile /
EXPOSE 53 53/udp

# Corefile contents:
.:53 {
    hosts /etc/hosts.docker {
        127.0.6.1 mydomain
        fallthrough
    }
    forward . 8.8.8.8
    log
    errors
}