CoreDNS es un servidor DNS escrito en Go cuya funcionalidad se compone mediante plugins declarados en un único fichero, el Corefile. Es el DNS predeterminado de Kubernetes, pero también sirve como resolvedor con caché o servidor autoritativo para una red interna. En esta guía instalarás CoreDNS en Ubuntu 24.04 como servicio systemd y lo configurarás como resolvedor con caché para tu red privada, con reenvío por dominio y una zona interna propia.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 512 MB de RAM.
- Un usuario no root con privilegios
sudo. - La subred privada desde la que los clientes consultarán el DNS. En los ejemplos se usa
10.0.0.0/8; sustitúyela por la tuya. digpara las pruebas, incluido en el paquetednsutils:
sudo apt update
sudo apt install dnsutils
Paso 1: Descargar e instalar el binario
CoreDNS no está empaquetado en los repositorios de Ubuntu con una versión actual, así que se usa el binario oficial de GitHub. Consulta la última versión en la página de releases y ajusta la variable si es más nueva que la indicada. Si tu servidor es ARM, cambia amd64 por arm64.
COREDNS_VERSION="1.14.7"
cd /tmp
curl -LO "https://github.com/coredns/coredns/releases/download/v${COREDNS_VERSION}/coredns_${COREDNS_VERSION}_linux_amd64.tgz"
curl -LO "https://github.com/coredns/coredns/releases/download/v${COREDNS_VERSION}/coredns_${COREDNS_VERSION}_linux_amd64.tgz.sha256"
Verifica la suma de comprobación antes de instalar nada:
sha256sum -c "coredns_${COREDNS_VERSION}_linux_amd64.tgz.sha256"
coredns_1.14.7_linux_amd64.tgz: OK
Extrae el binario e instálalo en /usr/local/bin:
tar xzf "coredns_${COREDNS_VERSION}_linux_amd64.tgz"
sudo install -m 755 coredns /usr/local/bin/coredns
coredns -version
CoreDNS-1.14.7
La segunda línea de la salida indica la plataforma y la versión de Go con la que se compiló. Puedes listar los plugins compilados en el binario con coredns -plugins.
Paso 2: Crear el usuario y los directorios
CoreDNS no necesita ejecutarse como root. Crea un usuario de sistema sin shell y los directorios de configuración y zonas:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin coredns
sudo mkdir -p /etc/coredns/zones
Los ficheros de /etc/coredns pertenecen a root y CoreDNS solo necesita leerlos, así que no hace falta cambiar su propietario.
Paso 3: Liberar el puerto 53
En Ubuntu 24.04, systemd-resolved ejecuta un stub DNS en 127.0.0.53:53 que impide a CoreDNS escuchar en todas las interfaces del puerto 53. Compruébalo:
sudo ss -lunp 'sport = :53'
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
UNCONN 0 0 127.0.0.54:53 0.0.0.0:* users:(("systemd-resolve",pid=612,fd=16))
UNCONN 0 0 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=612,fd=14))
En lugar de desinstalar systemd-resolved (del que dependen otros componentes del sistema), desactiva solo el stub con un fichero drop-in:
sudo mkdir -p /etc/systemd/resolved.conf.d
sudo nano /etc/systemd/resolved.conf.d/no-stub.conf
[Resolve]
DNSStubListener=no
Sin el stub, /etc/resolv.conf debe apuntar a la lista real de servidores que gestiona systemd-resolved, no a 127.0.0.53. Cambia el enlace simbólico y reinicia el servicio:
sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
sudo systemctl restart systemd-resolved
Comprueba que el puerto 53 está libre (el comando no debe devolver ninguna línea con :53) y que el servidor sigue resolviendo nombres:
sudo ss -lunp 'sport = :53'
getent hosts ubuntu.com
Paso 4: Escribir el Corefile
El Corefile se organiza en bloques de servidor: cada bloque empieza por la zona que atiende (. significa "todo lo demás") y contiene los plugins que se aplican. El orden de los plugins dentro del bloque no importa, porque CoreDNS los ejecuta en un orden fijo definido al compilar.
Crea el fichero:
sudo nano /etc/coredns/Corefile
. {
acl {
allow net 10.0.0.0/8 127.0.0.0/8
block
}
forward . 1.1.1.1 9.9.9.9 {
health_check 5s
}
cache 300
errors
log . {
class denial error
}
health 127.0.0.1:8080
ready 127.0.0.1:8181
prometheus 127.0.0.1:9153
reload
loop
}
Esto es lo que hace cada plugin:
acl: solo responde a la red privada y a localhost; el resto de clientes recibeREFUSED. Un resolvedor abierto a Internet se usa para ataques de amplificación DDoS, así que esta línea no es opcional.forward: reenvía las consultas que no puede responder a Cloudflare (1.1.1.1) y Quad9 (9.9.9.9), comprobando cada 5 segundos si siguen respondiendo.cache 300: guarda las respuestas hasta 300 segundos (o el TTL del registro, si es menor).errorsylog: registran errores y, para no llenar el journal, solo las consultas que terminan en NXDOMAIN o error.health,readyyprometheus: endpoints de estado y métricas, limitados a localhost.reload: vuelve a leer elCorefileautomáticamente cuando cambia (lo comprueba cada 30 segundos).loop: detecta bucles de reenvío y detiene CoreDNS en lugar de saturar la CPU.
Paso 5: Crear el servicio systemd
Crea la unidad de systemd:
sudo nano /etc/systemd/system/coredns.service
[Unit]
Description=CoreDNS DNS server
Documentation=https://coredns.io
After=network-online.target
Wants=network-online.target
[Service]
User=coredns
Group=coredns
ExecStart=/usr/local/bin/coredns -conf /etc/coredns/Corefile
ExecReload=/bin/kill -SIGUSR1 $MAINPID
Restart=on-failure
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
LimitNOFILE=1048576
[Install]
WantedBy=multi-user.target
AmbientCapabilities=CAP_NET_BIND_SERVICE permite al usuario coredns abrir el puerto 53 sin ser root, sin tener que marcar el binario con setcap (que habría que repetir en cada actualización). Las opciones Protect* impiden que el proceso escriba en el sistema de ficheros. SIGUSR1 hace que CoreDNS recargue el Corefile.
Arranca y habilita el servicio:
sudo systemctl daemon-reload
sudo systemctl enable --now coredns
systemctl status coredns --no-pager
● coredns.service - CoreDNS DNS server
Loaded: loaded (/etc/systemd/system/coredns.service; enabled; preset: enabled)
Active: active (running) since Thu 2026-09-24 11:02:41 UTC; 3s ago
Si el servicio no arranca, journalctl -u coredns -n 50 muestra el motivo (normalmente un error de sintaxis en el Corefile o el puerto 53 ocupado).
Paso 6: Probar la resolución y la caché
Consulta un dominio público a través de CoreDNS:
dig @127.0.0.1 ubuntu.com +noall +answer +stats | grep -E 'IN|Query time'
ubuntu.com. 300 IN A 185.125.190.20
;; Query time: 18 msec
Repite la consulta. La segunda respuesta sale de la caché: el tiempo baja a 0 ms y el TTL va disminuyendo:
ubuntu.com. 291 IN A 185.125.190.20
;; Query time: 0 msec
Comprueba también los endpoints de estado:
curl -s http://127.0.0.1:8080/health; echo
curl -s http://127.0.0.1:8181/ready; echo
OK
OK
Paso 7: Añadir una zona interna autoritativa
Con el plugin file, CoreDNS responde con autoridad para una zona a partir de un fichero en formato estándar (RFC 1035), el mismo que usa BIND. En este ejemplo se usa corp.internal: el dominio de nivel superior .internal está reservado para uso privado, por lo que nunca chocará con un dominio real.
Crea el fichero de zona:
sudo nano /etc/coredns/zones/db.corp.internal
$ORIGIN corp.internal.
$TTL 300
@ IN SOA ns1.corp.internal. hostmaster.corp.internal. (
2026092401 ; serial (AAAAMMDDNN)
3600 ; refresh
900 ; retry
604800 ; expire
300 ) ; TTL negativo
@ IN NS ns1.corp.internal.
ns1 IN A 10.0.0.53
web IN A 10.0.1.10
api IN A 10.0.1.20
db IN A 10.0.1.30
www IN CNAME web.corp.internal.
Sustituye 10.0.0.53 por la IP privada de tu servidor CoreDNS y los demás registros por los tuyos. Después, añade un bloque para la zona al final del Corefile:
sudo nano /etc/coredns/Corefile
corp.internal {
acl {
allow net 10.0.0.0/8 127.0.0.0/8
block
}
file /etc/coredns/zones/db.corp.internal {
reload 30s
}
errors
}
CoreDNS elige siempre el bloque con la zona más específica, así que las consultas a corp.internal las atiende este bloque y el resto sigue yendo al bloque .. La opción reload 30s vuelve a leer el fichero de zona cuando cambia su número de serie, por lo que para publicar cambios basta con editar los registros e incrementar el serial.
El plugin reload del bloque . aplicará el nuevo Corefile en unos 30 segundos. Para aplicarlo ya:
sudo systemctl reload coredns
Comprueba los registros:
dig @127.0.0.1 web.corp.internal +short
dig @127.0.0.1 www.corp.internal +short
10.0.1.10
web.corp.internal.
10.0.1.10
La respuesta debe llevar el indicador aa (authoritative answer). Compruébalo con dig @127.0.0.1 api.corp.internal | grep flags.
Paso 8: Reenviar un dominio a otro servidor DNS
Es habitual que un dominio concreto lo resuelva otro servidor: un Active Directory, una VPN o el DNS de un clúster. Para eso basta un bloque con su propio forward. Por ejemplo, para enviar ad.example.com a los controladores de dominio 10.0.5.10 y 10.0.5.11, añade al Corefile:
ad.example.com {
acl {
allow net 10.0.0.0/8 127.0.0.0/8
block
}
forward . 10.0.5.10 10.0.5.11
cache 60
errors
}
Recarga con sudo systemctl reload coredns y prueba con dig @127.0.0.1 dc01.ad.example.com.
Añadir entradas estáticas con hosts
Para unos pocos nombres sueltos no hace falta una zona: el plugin hosts acepta entradas en línea con el formato de /etc/hosts. Añádelo dentro del bloque .:
hosts {
10.0.2.50 legacy-app.lan
10.0.2.51 old-db.lan
fallthrough
}
fallthrough es importante: sin él, cualquier nombre que no esté en la lista devolvería NXDOMAIN en lugar de pasar a forward.
Paso 9: Abrir el firewall y usar CoreDNS desde los clientes
Permite el DNS solo desde la red privada. Sustituye la subred por la tuya:
sudo ufw allow from 10.0.0.0/8 to any port 53 proto udp
sudo ufw allow from 10.0.0.0/8 to any port 53 proto tcp
No abras los puertos 8080, 8181 y 9153: en esta configuración solo escuchan en localhost.
Desde otro servidor de la red privada, sustituye coredns_private_ip por la IP privada de CoreDNS y comprueba que responde:
dig @coredns_private_ip web.corp.internal +short
10.0.1.10
Para que un cliente Ubuntu use CoreDNS de forma permanente, configura el servidor DNS de su interfaz en Netplan (clave nameservers.addresses) y aplica los cambios con sudo netplan apply.
CoreDNS en Kubernetes
En Kubernetes, CoreDNS se ejecuta como Deployment en el namespace kube-system y su Corefile vive en un ConfigMap. Puedes ver la configuración actual con:
kubectl -n kube-system get configmap coredns -o yaml
Para reenviar un dominio interno (por ejemplo corp.internal hacia el servidor que acabas de montar), edita el ConfigMap y añade un bloque nuevo junto al .:53 existente, sin tocar este:
kubectl -n kube-system edit configmap coredns
corp.internal:53 {
errors
cache 30
forward . 10.0.0.53
}
El Corefile predeterminado de Kubernetes incluye el plugin reload, así que los pods de CoreDNS aplican el cambio en uno o dos minutos sin reiniciarlos. En clústeres gestionados (por ejemplo, con un addon que reescribe el ConfigMap) consulta la documentación de la distribución, porque tus cambios podrían sobrescribirse.
Solución de problemas
Listen: listen tcp :53: bind: address already in use
Otro proceso ocupa el puerto 53. Identifícalo con sudo ss -lntup 'sport = :53'. Si es systemd-resolve, revisa el Paso 3; si es dnsmasq o named, detenlo y deshabilítalo.
Loop detected en el journal
El plugin loop ha detectado que CoreDNS se reenvía consultas a sí mismo. Suele pasar si usas forward . /etc/resolv.conf y ese fichero apunta a 127.0.0.1. Usa servidores upstream explícitos, como en el Paso 4.
Los clientes reciben REFUSED
La IP del cliente no está permitida en el plugin acl. Revisa la subred en cada bloque y recarga el servicio.
Un cambio en el fichero de zona no se aplica
El plugin file solo recarga la zona si el número de serie del registro SOA aumenta. Increméntalo en cada cambio y consulta journalctl -u coredns -n 20 para ver si hay errores de sintaxis en la zona.
Conclusión
Tienes CoreDNS funcionando en Ubuntu 24.04 como servicio systemd sin privilegios de root, actuando como resolvedor con caché restringido a tu red privada, con una zona interna autoritativa y reenvío por dominio. Como siguientes pasos, puedes añadir un segundo servidor CoreDNS con la misma configuración para tener redundancia, recoger las métricas del puerto 9153 con Prometheus o cifrar el reenvío hacia los upstream con DNS over TLS (forward . tls://1.1.1.1 con tls_servername cloudflare-dns.com).
