La configuración de DNS sigue rompiendo producción
DNS parece simple hasta que falla. Cómo las actualizaciones de macOS, resolv.conf, mDNS y el TLD .internal causan caídas en producción que nadie ve venir.

La última versión de macOS de Apple rompió la configuración personalizada de DNS de miles de desarrolladores. En concreto, empezó a interceptar las consultas para el TLD .internal, que muchas empresas usan para recursos de red privada, y a enviarlas a mDNS (multicast DNS) en lugar de al resolver DNS configurado. Servicios que llevaban años funcionando de repente eran inalcanzables. La solución no era obvia, los mensajes de error no ayudaban y, como suele pasar con los problemas de DNS, los síntomas parecían todo menos DNS.
Esta es una historia sobre la configuración de DNS, un tema con el que todo desarrollador se topa, que pocos entienden a fondo y que todos subestimamos hasta que rompe algo en producción a las 2 de la madrugada.
El incidente del TLD .internal
Muchas organizaciones usan .internal como TLD privado para sus servicios internos: api.internal, db.staging.internal, grafana.internal. Parecía una elección segura: no es un TLD público, indica claramente que algo es interno y se ha usado así durante décadas.
Entonces el nuevo sistema operativo de Apple decidió que .internal debía gestionarse con mDNS, el mismo protocolo que resuelve myprinter.local en tu red doméstica. Cuando tu Mac intenta resolver api.internal, en lugar de preguntar al servidor DNS configurado, envía una consulta mDNS por la red local. Tu servidor DNS nunca ve la consulta. La respuesta es nada (timeout) o una dirección incorrecta, si algo en la red local responde a ese nombre.
Los síntomas: curl api.internal se queda colgado 5 segundos y luego falla. Las conexiones SSB a hosts internos caducan. Los usuarios de VPN no pueden llegar a los recursos internos. Los contenedores de Docker no resuelven los nombres de servicio. Y como el fallo se manifiesta como un timeout de conexión genérico, la mayoría de los desarrolladores empieza a depurar la VPN, el firewall o la aplicación, cualquier cosa menos DNS.
Cómo funciona realmente la resolución DNS en los sistemas modernos
El modelo mental de «DNS es simple» (tu ordenador pregunta a un servidor DNS por una IP y el servidor responde) lleva años sin ser preciso. Los sistemas modernos tienen un pipeline de resolución con múltiples etapas, y cada una puede interceptar, modificar o redirigir consultas.
DNS resolution pipeline on a typical macOS/Linux system:
Application calls getaddrinfo("api.internal")
↓
1. /etc/hosts — checked first. Static overrides.
↓
2. NSSwitch / resolver configuration
- Linux: /etc/nsswitch.conf controls lookup order
- macOS: /etc/resolver/* for per-domain DNS servers
↓
3. System resolver (systemd-resolved, mDNSResponder)
- May intercept .local, .internal, or other special TLDs
- May apply DNSSEC validation
- May cache responses
↓
4. DNS server (from DHCP, VPN, or manual config)
- Queries sent to configured upstream resolver
- May go through a corporate DNS proxy
↓
5. Recursive resolver (8.8.8.8, 1.1.1.1, corporate)
- Walks the DNS hierarchy: root → TLD → authoritative
↓
Response flows back up the chain
En cada etapa puede salir algo mal. El resolver del sistema puede interceptar una consulta antes de que llegue al servidor DNS configurado. La VPN puede sobrescribir la configuración de DNS. DHCP puede inyectar servidores DNS que tengan prioridad sobre tu configuración manual. Y la caché en cada nivel implica que una corrección puede no surtir efecto de inmediato.
La mentira de /etc/resolv.conf
En Linux, /etc/resolv.conf fue históricamente la única fuente de verdad para la configuración de DNS. Pones tu nameserver ahí y el sistema lo usa. Sencillo.
Las distribuciones modernas de Linux lo han complicado bastante. Si usas systemd-resolved (que Ubuntu, Fedora y la mayoría de distros basadas en systemd activan por defecto), /etc/resolv.conf es un enlace simbólico a un stub resolver en 127.0.0.53. Tu configuración de DNS real vive en el estado de systemd-resolved, que se gestiona con resolvectl. Editar /etc/resolv.conf directamente, o bien se sobrescribe en el siguiente cambio de red, o bien rompe el stub resolver.
# What you THINK your DNS config is:
$ cat /etc/resolv.conf
nameserver 127.0.0.53 # This is systemd-resolved's stub
# What your DNS config ACTUALLY is:
$ resolvectl status
Global:
DNS Servers: 8.8.8.8 8.8.4.4
DNS Domain: ~.
Link 2 (eth0):
DNS Servers: 10.0.0.1 # From DHCP — this takes precedence
DNS Domain: corp.internal
Link 5 (wg0): # WireGuard VPN
DNS Servers: 10.100.0.1
DNS Domain: ~internal # VPN claims the .internal domain
# Three different DNS servers, each handling different domains.
# /etc/resolv.conf shows none of this.
Docker añade otra capa. Los contenedores de Docker tienen su propio /etc/resolv.conf, normalmente copiado de la configuración del host cuando el contenedor arranca. Si el DNS del host cambia (al conectar la VPN, al cambiar de red), los contenedores en ejecución siguen con la configuración antigua hasta que se reinician. Es una fuente habitual de errores del tipo «funciona al reiniciar pero falla al cabo de un rato».
Split DNS y conflictos con la VPN
El split DNS, que consiste en usar servidores DNS distintos para dominios distintos, es estándar en entornos corporativos. Tu VPN envía las consultas de *.corp.internal al servidor DNS corporativo, mientras que las públicas van a tu resolver habitual. Funciona muy bien cuando está bien configurado y falla de formas muy confusas cuando no lo está.
Modos de fallo habituales:
- La VPN establece sus servidores DNS como predeterminados globales. Algunos clientes de VPN configuran el servidor DNS de la VPN como resolver por defecto para todas las consultas, no solo las internas. Así, todas tus consultas DNS, incluida la navegación personal, pasan por el DNS corporativo. Esto es lento (porque el DNS corporativo está en otro centro de datos) y supone un problema de privacidad.
- Conflictos en el orden de los servidores DNS. Cuando hay varios servidores DNS configurados, el sistema los prueba en orden. Si el primero es lento o inalcanzable (la VPN está desconectada pero la configuración de DNS persiste), cada consulta espera a que expire el timeout antes de probar el segundo. Eso añade 5 segundos de retraso a cada conexión.
- Los dominios de búsqueda se añaden en silencio. Un dominio de búsqueda
corp.internalhace que una consulta paraapitambién pruebeapi.corp.internal. Es útil (escribesssh apien lugar dessh api.corp.internal) hasta que deja de serlo: cuando un nombre de host corto colisiona con el nombre de un servicio interno, las consultas se resuelven a la IP equivocada. - mDNS intercepta los dominios internos. Como muestra el incidente de
.internal, el resolver del sistema puede interceptar consultas para ciertos TLD antes de que lleguen a cualquier servidor DNS configurado. Es especialmente traicionero porque la interceptación ocurre en silencio: sin mensaje de error, solo un timeout o una respuesta incorrecta.
Depurar DNS: la caja de herramientas
Cuando DNS falla, necesitas herramientas que te muestren exactamente qué pasa en cada etapa del pipeline de resolución.
# 1. Check what your system actually resolves:
$ dig api.internal
# Shows: query sent to which server, response received
# The 'SERVER' line tells you which DNS server answered
# 2. Query a specific DNS server directly:
$ dig @10.100.0.1 api.internal
# Bypasses the system resolver — goes directly to the specified server
# If this works but 'dig api.internal' doesn't, the problem is
# in the system resolver, not the DNS server
# 3. Check systemd-resolved state (Linux):
$ resolvectl query api.internal
# Shows which interface/server handled the query
# 4. Check macOS DNS routing:
$ scutil --dns
# Shows per-domain DNS configuration on macOS
# Look for your .internal domain — is it being routed correctly?
# 5. Monitor DNS queries in real time:
$ sudo tcpdump -i any port 53
# See actual DNS packets. If no packets go out for your query,
# the system resolver is intercepting it locally.
# 6. Flush DNS cache (when fixing config):
$ sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder # macOS
$ resolvectl flush-caches # Linux with systemd-resolved
El paso de diagnóstico más importante: consulta tu servidor DNS directamente con dig @servidor nombredehost. Si eso funciona pero la resolución normal no, el problema está entre tu aplicación y el servidor DNS: interceptación por el resolver del sistema, caché o enrutamiento. Si las consultas directas también fallan, el problema es el propio servidor DNS o la conectividad de red hacia él.
Elegir tu TLD privado
El incidente de .internal plantea la pregunta práctica: ¿qué TLD deberías usar para tus servicios internos? No hay una respuesta universalmente segura, pero algunas opciones son mejores que otras.
- .internal: RFC 6762 lo reserva para uso interno, pero el manejo de mDNS de Apple lo convierte en un problema en macOS. Puede funcionar bien en Linux.
- .local: reservado para mDNS (RFC 6762). Usarlo para servicios resueltos por DNS entrará en conflicto con Bonjour/Avahi. No lo uses.
- .corp, .home, .mail: no están reservados. La ICANN podría asignarlos como TLD públicos en cualquier momento, y eso rompería tu resolución interna. No los uses.
- Subdominio de un dominio que tengas.
internal.tuempresa.com: es la opción más segura. Controlas el dominio padre, así que no hay riesgo de colisión. La desventaja: es más largo de escribir. - .test, .example, .invalid, .localhost: reservados por la RFC 6761 y garantizados para nunca asignarse como TLD públicos.
.testes la opción más limpia para servicios internos que no son de producción.
La respuesta aburrida pero correcta: usa un subdominio de un dominio que tengas. api.internal.tuempresa.com no es ambiguo, no colisiona con nada y funciona correctamente en cualquier sistema operativo y resolver DNS. Es más largo, pero los problemas de DNS a las 2 de la madrugada son peores que teclear unos caracteres de más.
Lecciones para sistemas en producción
Cada caída de DNS enseña las mismas lecciones, y no paramos de reaprenderlas.
- Monitoriza la resolución DNS, no solo los servidores DNS. Que tu servidor DNS esté sano no significa que la resolución funcione. Monitoriza el camino completo: ¿tu aplicación puede realmente resolver los nombres de host que necesita? Una comprobación sintética que resuelva los nombres críticos cada 30 segundos detecta los problemas antes que la monitorización del servidor.
- Usa TTL cortos para el DNS interno. Cuando necesitas cambiar un registro DNS interno, los TTL largos mantienen datos obsoletos en caché. Los TTL de 60 segundos para servicios internos te dan una conmutación rápida con poca carga en los resolvers.
- Prueba las actualizaciones del sistema operativo contra tu configuración de DNS. La rotura de
.internalsolo afectó a usuarios de macOS. Prueba tu entorno de desarrollo tras las actualizaciones principales del sistema operativo: los cambios en la resolución DNS rara vez aparecen en las notas de la versión. - Documenta tu arquitectura DNS. ¿Qué servidores DNS gestionan qué dominios? ¿Dónde están los puntos de división? ¿Qué pasa cuando la VPN se desconecta? Esta documentación es lo primero que necesitarás durante una caída y lo último que alguien escribe.
- Ten un plan B. Si la resolución DNS falla para un servicio interno, ¿puede tu aplicación usar una IP fija? No es elegante, pero una IP fija que funciona es mejor que un nombre DNS que no resuelve.
DNS es uno de esos servicios fundamentales que son invisibles cuando funcionan y catastróficos cuando no. La rotura de .internal es un recordatorio de que la configuración de DNS es más frágil de lo que parece: una sola actualización del sistema operativo puede cambiar el comportamiento de resolución de millones de usuarios. Diseña tus sistemas asumiendo que DNS va a fallar, y te sorprenderá menos cuando lo haga.


