This guide uses Docker to run CoreDNS in a containerized environment. This approach provides isolation, easy updates, and simplified deployment.
For Docker installation, see Docker.
Create a directory to store your configuration and compose files.
mkdir -p /opt/coredns/config
cd /opt/coredns
Create a basic Corefile for your DNS server:
cat <<'EOF' > /opt/coredns/config/Corefile
.:53 {
errors
log
health
ready
# Forward queries to upstream resolvers
forward . 1.1.1.1 8.8.8.8 {
max_fails 3
expire 10s
except localhost
}
# Cache responses for performance
cache 30
# Prevent loops
loop
# Load balancing for responses
loadbalance
}
EOF
Create a docker-compose.yml file with proper configuration:
version: '3.8'
services:
coredns:
image: coredns/coredns:1.14.1
container_name: coredns
restart: unless-stopped
network_mode: "host" # Required to bind to port 53
volumes:
- ./config:/etc/coredns:ro
command: -conf /etc/coredns/Corefile
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
read_only: true
tmpfs:
- /tmp
labels:
- "description=CoreDNS server"
- "version=1.14.1"
⚠️ Security Note: Using
network_mode: "host"bypasses Docker’s network isolation, giving the container access to the host’s network stack. This is required for DNS servers that need to bind to port 53 on all interfaces, but it reduces container isolation. For better isolation, consider:
- Running on a dedicated DNS interface
- Using port mapping with
cap_add: NET_BIND_SERVICEand binding to non-privileged ports- Implementing strict firewall rules to limit network access
- Using rootless Docker with appropriate capabilities
Start the container in the background:
docker-compose up -d
If you prefer not to use Docker Compose:
docker run -d \
--name coredns \
--restart=unless-stopped \
--network=host \
--security-opt=no-new-privileges:true \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--read-only \
--tmpfs /tmp \
-v /opt/coredns/config:/etc/coredns:ro \
coredns/coredns:1.14.1 -conf /etc/coredns/Corefile
Check that the container is running:
docker ps | grep coredns
Test DNS resolution:
dig @127.0.0.1 google.com
For production environments, consider these security measures:
version: '3.8'
services:
coredns:
image: coredns/coredns:1.14.1
container_name: coredns
restart: unless-stopped
network_mode: "host"
volumes:
- ./config:/etc/coredns:ro
command: -conf /etc/coredns/Corefile
security_opt:
- no-new-privileges:true
- apparmor:docker-default
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
read_only: true
tmpfs:
- /tmp
- /var/run
user: "65534:65534" # nobody user
labels:
- "description=CoreDNS server"
- "version=1.14.1"
Add monitoring plugins to your Corefile:
.:53 {
errors
log
health
ready
# Enable Prometheus metrics
prometheus :9153
forward . 1.1.1.1 8.8.8.8 {
max_fails 3
expire 10s
}
cache 30
loop
loadbalance
}
Then expose the metrics port in your compose file:
ports:
- "9153:9153" # Prometheus metrics
docker logs coredns
docker logs coredns --follow # Follow logs in real-time
To update to a newer version:
docker-compose pulldocker-compose up -dTo update configuration:
docker-compose restartIf you encounter permission errors binding to port 53:
NET_BIND_SERVICE capability is addedCheck the logs for specific error messages:
docker logs coredns
Common issues:
Test from inside the container:
docker exec -it coredns nslookup google.com
Running CoreDNS in containers for production? We help with:
Need help? office@linux-server-admin.com or Contact Us