Cambias un registro DNS. Esperas. Incluso haces el ritual: “está bien, el TTL es bajo.”
Y sin embargo, un contenedor sigue llamando a la IP antigua como si tuviera una relación a distancia con tu equilibrador de carga jubilado.
La trampa es sencilla: estás vaciando un caché, no el caché. Docker añade al menos una capa de resolución, Linux añade más, y tu aplicación puede estar acumulando respuestas como si cobrara alquiler por cada consulta. Si no sabes dónde vive la mentira, seguirás persiguiendo fantasmas.
Un modelo mental práctico: dónde pueden quedarse las respuestas DNS
El troubleshooting DNS en contenedores rara vez se trata de “el DNS está caído”. Se trata de tiempo, contexto y caché en lugares que olvidaste que existían.
La forma más rápida de salir del atasco es dejar de preguntar “¿cuál es mi DNS?” y empezar a preguntar “¿quién respondió, quién almacenó en caché y quién sigue reutilizándolo?”
DNS tiene múltiples “clientes”, no uno solo
Una aplicación en contenedor normalmente no habla directamente con los servidores DNS corporativos. Habla con algo más cercano:
- La propia lógica del resolvedor de la aplicación (JVM, Go net.Resolver behavior, Node’s caching, Envoy, reglas de resolución de nginx upstream).
- libc + NSS (el resolvedor de glibc, musl,
/etc/nsswitch.conf,/etc/hosts, dominios de búsqueda,ndots, timeout/intentos). - Un demonio de caché local (nscd, dnsmasq, systemd-resolved) — a veces dentro del contenedor, a menudo en el host.
- DNS embebido de Docker (comúnmente en
127.0.0.11) que reenvía y hace descubrimiento de servicios nombre→contenedor en redes definidas por el usuario. - Los resolutores upstream del host (DNS corporativo, resolvedor VPC, CoreDNS, Unbound, bind).
Si vacías la caché del host pero la aplicación cachea para siempre, nada cambia. Si reinicias el contenedor pero el resolvedor embebido de Docker sigue devolviendo algo extraño, tampoco cambia nada.
Necesitas aislar qué capa está sirviendo respuestas obsoletas.
“Vaciar DNS” no es un único comando
Sólo en Linux, “vaciar DNS” puede significar:
- reiniciar
systemd-resolvedo vaciar sus cachés, - reiniciar
nscdodnsmasq, - reiniciar Docker (o solo los contenedores/redes afectadas),
- reiniciar el proceso de la aplicación para limpiar su caché interno,
- o vaciar un resolutor caché upstream que no controlas.
El vaciado correcto es el que realmente cambia la ruta de la siguiente consulta.
Un principio operativo que envejece bien: observa antes de intervenir. Vaciar oculta evidencias. Reúne algunas instantáneas “antes”, luego vacía lo mínimo necesario.
Una cita que vale la pena mantener cerca de tu cerebro de on-call: “Hope is not a strategy.” — General Gordon R. Sullivan
Datos interesantes y un poco de historia (para que dejes de culpar a la “magia de Docker”)
- El caché DNS no siempre fue esperado en la capa OS. Los resolvedores Unix tempranos tendían a ser resolvedores stub simples; el caché se volvió común a medida que las redes se hicieron más lentas y las búsquedas de nombres frecuentes.
- El TTL es orientativo, no una ley universal. Un resolvedor puede limitar el TTL (mín/max), y las aplicaciones pueden cachear más tiempo de lo que DNS indica —especialmente cuando intentan “ayudar”.
- Docker añadió DNS embebido para hacer viable el descubrimiento de servicios en redes definidas por el usuario. De otro modo, la resolución contenedor→contenedor se vuelve incómoda rápidamente.
- La red por defecto bridge de Docker históricamente copiaba el comportamiento de resolv.conf del host. Eso hizo que los contenedores heredaran rarezas del DNS del host —buenas y malas— hasta que el DNS embebido se volvió la norma en redes personalizadas.
- systemd-resolved cambió expectativas en muchas distribuciones. Introdujo un resolvedor stub local (a menudo
127.0.0.53) con DNS dividido y caché, que interactúa con Docker de formas sorprendentes. - El comportamiento del resolvedor en Alpine (musl) difiere del de Debian/Ubuntu (glibc). Los modos de fallo alrededor de dominios de búsqueda,
ndotsy timeouts pueden parecer “DNS aleatorio”. - El comportamiento del resolvedor en Go cambió entre versiones. Dependiendo de flags de compilación y entorno, puede usar el resolvedor en Go puro o cgo (glibc), afectando caché y análisis de configuración.
- El DNS corporativo frecuentemente tiene su propio caché y reescrituras “útiles”. DNS con horizonte dividido, zonas internas y respuestas basadas en políticas significan que el mismo nombre puede resolverse diferente dentro/fuera de una VPN.
Ese último punto importa porque “funciona en mi laptop” puede ser literalmente cierto: tu laptop está en una vista DNS diferente a la del host del contenedor.
La pila de caché DNS: dentro del contenedor vs host vs upstream
Layer 0: la propia aplicación (la mentirosa más común)
Si solo recuerdas una cosa: muchas aplicaciones cachean DNS más tiempo del que crees, y algunas efectivamente cachean para siempre a menos que vuelvas a resolver.
Patrones comunes:
- Pooles de conexiones mantienen sockets hacia IPs antiguas. DNS puede cambiar y a la app no le importará hasta que el pool se renueve.
- Cachés de DNS en tiempo de ejecución (cacheo de InetAddress de Java, algunos clientes HTTP, mallas de servicios).
- Procesos de larga duración que resuelven una vez al inicio y nunca más.
Consecuencia operativa: vaciar el DNS debajo de la app no hará nada si la app nunca pregunta de nuevo.
Tu “vaciado” mejor puede ser un reinicio gradual o una señal que fuerce recarga (si está soportado).
Layer 1: libc, NSS y configuración del resolvedor
Aquí es donde viven /etc/resolv.conf, /etc/nsswitch.conf, /etc/hosts, dominios de búsqueda y opciones como ndots.
Esta capa no suele “cachear” agresivamente por sí misma (glibc no mantiene un gran caché compartido), pero puede crear comportamientos que parezcan caché:
- search domains + ndots causan múltiples consultas por búsqueda. Una de esas consultas puede tener éxito y quedarse en caché en upstream.
- timeout/intentos causan bloqueos largos que parecen fallos parciales.
- /etc/hosts sobrescribe DNS por completo, resultando en “DNS obsoleto” que en realidad es un archivo estático.
Layer 2: demonios caché (nscd, dnsmasq, systemd-resolved)
Si un nodo ejecuta un resolvedor caché local, los contenedores pueden consultarlo directamente (vía resolv.conf copiado) o indirectamente (Docker reenviando a él).
Si ese demonio está cacheando datos malos, los contenedores seguirán viéndolos hasta que el caché del demonio expire o lo limpies.
En Ubuntu moderno, systemd-resolved frecuentemente se sitúa en 127.0.0.53, actuando como stub y caché local.
Docker a veces tiene problemas si copia 127.0.0.53 dentro de los contenedores: esa dirección está dentro del namespace del contenedor y no apunta al stub del host salvo que exista ruteo especial.
Ese modo de fallo no es caché; es un desajuste de namespaces.
Layer 3: DNS embebido de Docker (127.0.0.11)
En redes bridge definidas por el usuario, Docker normalmente inyecta nameserver 127.0.0.11 en el /etc/resolv.conf del contenedor.
Esa IP no es el resolvedor del host; es el resolvedor interno del motor Docker ligado dentro del namespace de red del contenedor.
Lo que hace bien:
- resuelve nombres de contenedores a IPs dentro de la red Docker,
- gestiona aliases y descubrimiento de servicios en Compose,
- reenvía otras consultas a los resolutores upstream configurados para Docker/host.
Lo que hace mal (o al menos de forma opaca):
- se convierte en un salto extra donde timeouts y comportamiento de caché pueden ser malinterpretados,
- oculta cambios en los resolutores upstream a menos que los contenedores se recreuen o Docker recargue su configuración,
- hace que “vaciar DNS” sea ambiguo porque no gestionas ese caché directamente como un demonio típico.
Layer 4: resolutores upstream (CoreDNS, Unbound, bind, resolutores de nube)
Los resolutores upstream cachean según TTL, pero además tienen:
- caché negativa (NXDOMAIN cacheado por un tiempo),
- prefetch y características de servir-stale en algunas implementaciones,
- política / DNS con horizonte dividido que cambia respuestas según la red de origen.
Si no controlas el caché upstream, el único “vaciado” fiable es consultar un resolutor distinto (temporalmente) o esperar a que expire el TTL/caché negativa.
Broma #1: DNS significa “Definitely Not Synchronized.” No lo está, pero explica la mayoría de los fines de semana de on-call.
Guía rápida de diagnóstico
El objetivo aquí no es convertirte en un erudito DNS. Es encontrar el cuello de botella y al mentiroso rápido, con el mínimo daño colateral.
Primero: verifica el síntoma y el alcance
- ¿Es un contenedor, un host o todos los hosts? Problemas de un solo contenedor suelen significar caché de la app, configuración del contenedor o ruta de resolvedor específica del namespace.
- ¿Es un nombre o todos los nombres? Un solo nombre apunta a registro DNS o caché; todos los nombres apuntan a conectividad o configuración del resolvedor.
- ¿Es obsoleto (IP antigua) o fallo (NXDOMAIN/timeouts)? Obsoleto es caché; timeouts son red/MTU/firewall; NXDOMAIN puede ser caché negativa o DNS con horizonte dividido.
Segundo: identifica qué resolvedor usa el contenedor
- Revisa
/etc/resolv.confen el contenedor:127.0.0.11significa DNS embebido de Docker; cualquier otra cosa es directo. - Revisa el modo de red de Docker y si es una bridge definida por el usuario, red host, o algo inusual.
Tercero: haz consultas lado a lado desde contenedor y host
- Consulta el nombre desde el contenedor usando
digcontra el resolvedor configurado. - Consulta el mismo nombre desde el host contra los resolutores upstream.
- Si las respuestas difieren, la divergencia está en algún punto entre el stub del contenedor y el upstream. Si las respuestas coinciden pero la app sigue llamando a la IP antigua, el mentiroso es la app o el pool de conexiones.
Cuarto: decide el reinicio mínimo que cambie el comportamiento
- Si la app cachea: reinicia el proceso o fuerza re-resolución (si está soportado).
- Si la caché del host: vacía systemd-resolved/dnsmasq/nscd.
- Si la ruta del DNS embebido de Docker es sospechosa: recrea el contenedor (o la red), o ajusta la configuración DNS del daemon de Docker y reinicia Docker en una ventana controlada.
- Si el caché upstream: consulta un resolutor distinto temporalmente, o espera a que expire el TTL/caché negativa.
Tareas prácticas: comandos, significado de la salida y decisiones (12+)
Task 1: Find the container’s configured resolver
cr0x@server:~$ docker exec -it web-1 cat /etc/resolv.conf
nameserver 127.0.0.11
options ndots:0
Qué significa: Este contenedor usa el DNS embebido de Docker (127.0.0.11). Vaciando las cachés del host puede que no cambie directamente lo que ve si Docker está cacheando/reenviando de forma extraña.
Decisión: Consulta con dig dentro del contenedor para ver qué devuelve el DNS de Docker; también inspecciona la configuración DNS del daemon de Docker.
Task 2: Confirm whether the container is on a user-defined network
cr0x@server:~$ docker inspect -f '{{json .NetworkSettings.Networks}}' web-1
{"app_net":{"IPAMConfig":null,"Links":null,"Aliases":["web-1","web"],"NetworkID":"8d0e...","EndpointID":"0c4c...","Gateway":"172.20.0.1","IPAddress":"172.20.0.10","IPPrefixLen":16,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"02:42:ac:14:00:0a","DriverOpts":null}}
Qué significa: Red bridge definida por el usuario (app_net) suele implicar que el DNS embebido está activo y que el descubrimiento de servicios está en juego.
Decisión: Si el problema es “el nombre del servicio apunta al contenedor equivocado”, estás depurando DNS de red Docker, no DNS corporativo.
Task 3: Compare DNS answers inside the container using dig
cr0x@server:~$ docker exec -it web-1 sh -lc 'apk add --no-cache bind-tools >/dev/null 2>&1 || true; dig +noall +answer api.internal A'
api.internal. 30 IN A 10.40.12.34
Qué significa: El resolvedor de Docker (o lo que reenvía) actualmente devuelve 10.40.12.34 con TTL 30 segundos.
Decisión: Si esperabas una IP nueva y el TTL ya pasó, algo está cacheando por encima/por debajo. Ahora consulta desde el host y desde un resolutor upstream específico.
Task 4: Query the same name from the host against its default resolver
cr0x@server:~$ dig +noall +answer api.internal A
api.internal. 30 IN A 10.40.12.99
Qué significa: El host ve 10.40.12.99, el contenedor ve 10.40.12.34. La divergencia es real y local.
Decisión: El resolvedor embebido de Docker podría estar reenviando a resolutores upstream diferentes que el host, o cacheando datos obsoletos.
Task 5: Find what DNS servers Docker daemon is using
cr0x@server:~$ docker info | sed -n '/DNS:/,/Registry Mirrors:/p'
DNS: 10.0.0.2
10.0.0.3
Qué significa: Docker está configurado para usar 10.0.0.2 y 10.0.0.3 como resolutores upstream para el reenvío del DNS embebido.
Decisión: Consulta esos resolutores directamente desde el host para ver qué devuelven y si tienen caché obsoleto.
Task 6: Query Docker’s upstream resolver directly
cr0x@server:~$ dig @10.0.0.2 +noall +answer api.internal A
api.internal. 30 IN A 10.40.12.34
Qué significa: El resolutor upstream que usa Docker está devolviendo la IP antigua. Docker no necesariamente miente; está repitiendo fielmente la mentira de otro.
Decisión: Arregla o vacía la caché del resolutor upstream, o redirige Docker a los resolutores correctos.
Task 7: Detect systemd-resolved involvement on the host
cr0x@server:~$ resolvectl status | sed -n '1,35p'
Global
Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
Current DNS Server: 10.0.0.2
DNS Servers: 10.0.0.2 10.0.0.3
DNS Domain: corp.internal
Qué significa: El host usa systemd-resolved en modo stub; puede estar cacheando, y también puede ser la fuente de los contenidos de /etc/resolv.conf que Docker lee.
Decisión: Si los contenedores copian una dirección stub que es inaccesible en su namespace, arregla la configuración DNS de Docker; si el cacheo del host es malo, vacía resolved.
Task 8: Flush systemd-resolved cache on the host (when it is the cache)
cr0x@server:~$ sudo resolvectl flush-caches
cr0x@server:~$ resolvectl statistics | sed -n '1,25p'
DNSSEC supported by current servers: no
Transactions: 1482
Cache Hits: 312
Cache Misses: 1170
Qué significa: Se vació la caché; las búsquedas posteriores deberían ser misses hasta que la caché se caliente.
Decisión: Vuelve a ejecutar consultas en host/contenedor. Si el contenedor aún ve respuestas antiguas, systemd-resolved no fue el cuello de botella.
Task 9: Check whether the container can even reach the host’s stub resolver
cr0x@server:~$ docker exec -it web-1 sh -lc 'grep -E "^nameserver" /etc/resolv.conf; nc -zu -w1 127.0.0.53 53; echo $?'
nameserver 127.0.0.11
1
Qué significa: El contenedor no está configurado para usar 127.0.0.53 de todos modos, y aun si lo estuviera, esa dirección sería local al namespace del contenedor.
Decisión: Deja de intentar “vaciar el stub del host” como solución para un contenedor que usa 127.0.0.11. Trabaja sobre el DNS de Docker/upstream.
Task 10: Determine if the application is pinning connections to old IPs
cr0x@server:~$ docker exec -it web-1 sh -lc 'ss -tnp | head -n 10'
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 0 172.20.0.10:49218 10.40.12.34:443 users:(("app",pid=1,fd=73))
ESTAB 0 0 172.20.0.10:49222 10.40.12.34:443 users:(("app",pid=1,fd=74))
Qué significa: La app está actualmente conectada a la IP antigua. Aunque DNS ahora resuelva la IP nueva, los sockets activos continúan en uso.
Decisión: Fuerza el churn de conexiones (recarga, reinicio, reducir keepalive), o arregla el comportamiento del pool. Vaciar DNS no cerrará sesiones TCP establecidas.
Task 11: Inspect /etc/hosts inside container for “DIY DNS” surprises
cr0x@server:~$ docker exec -it web-1 cat /etc/hosts
127.0.0.1 localhost
172.20.0.10 web-1
10.40.12.34 api.internal
Qué significa: Alguien fijó api.internal en /etc/hosts. Eso no es caché. Es una anulación permanente.
Decisión: Elimina la entrada (reconstruye la imagen, arregla el entrypoint o deja de inyectar registros hosts). Luego redepliega. Vaciar cualquier caché DNS es irrelevante hasta que esto se elimine.
Task 12: Check NSS order (hosts vs dns) inside container
cr0x@server:~$ docker exec -it web-1 sh -lc 'cat /etc/nsswitch.conf | sed -n "1,25p"'
passwd: files
group: files
hosts: files dns
networks: files
Qué significa: files se comprueba antes que DNS. Si /etc/hosts tiene una entrada, gana.
Decisión: Si debes usar /etc/hosts para una migración puntual, trátalo como una configuración gestionada por cambios, no como un parche que se olvida.
Task 13: Observe negative caching (NXDOMAIN) behavior
cr0x@server:~$ docker exec -it web-1 sh -lc 'dig +noall +authority does-not-exist.corp.internal'
corp.internal. 300 IN SOA ns1.corp.internal. hostmaster.corp.internal. 2026010301 3600 600 604800 300
Qué significa: El SOA negative TTL es 300 segundos. NXDOMAIN puede ser cacheado por resolutores por 5 minutos.
Decisión: Si acabas de crear el registro, esperar puede ser la opción correcta. O consulta los servidores autoritativos directamente si puedes.
Task 14: Check Docker daemon config for fixed DNS settings
cr0x@server:~$ sudo cat /etc/docker/daemon.json
{
"dns": ["10.0.0.2", "10.0.0.3"],
"log-driver": "json-file"
}
Qué significa: Los resolutores upstream de Docker están fijados. Si el DNS corporativo cambió, Docker no seguirá automáticamente el host.
Decisión: Actualiza este archivo (vía gestión de configuración), luego reinicia Docker en una ventana de mantenimiento y recrea contenedores si es necesario.
Task 15: Confirm what the host’s /etc/resolv.conf actually is
cr0x@server:~$ ls -l /etc/resolv.conf
lrwxrwxrwx 1 root root 39 Jan 3 09:12 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf
Qué significa: El host apunta al resolv.conf stub de systemd. Docker leer esto y copiarlo dentro de los contenedores puede ser desastroso si resulta en nameserver 127.0.0.53 dentro de contenedores.
Decisión: Prefiere configuraciones DNS explícitas para Docker (daemon.json) o asegúrate de que Docker use el resolv.conf “real” que tu distro provea (/run/systemd/resolve/resolv.conf) vía configuración del daemon.
Task 16: Test the path from container to resolver and measure latency
cr0x@server:~$ docker exec -it web-1 sh -lc 'time dig +tries=1 +timeout=2 api.internal A +noall +answer'
api.internal. 30 IN A 10.40.12.34
real 0m0.018s
user 0m0.009s
sys 0m0.004s
Qué significa: La consulta es rápida. Si tu app reporta “timeout DNS”, puede estar haciendo múltiples búsquedas (dominios de búsqueda) o usando una ruta de resolvedor diferente a tu herramienta de prueba.
Decisión: Revisa dominios de búsqueda y ndots; inspecciona comportamiento del resolvedor de la aplicación; captura tráfico si es necesario.
Tres micro-historias del mundo corporativo (anonimizadas)
Incidente: la suposición equivocada (“vaciamos DNS, así que debe estar bien”)
Una empresa SaaS mediana movió una API interna detrás de un nuevo VIP durante una limpieza del datacenter. Bajaron el TTL días antes, vigilaron dashboards y hicieron el corte un martes por la mañana porque nadie disfruta un viernes.
La mitad de la flota se movió sin problemas. Algunos pods de aplicaciones no. Los errores subieron, pero solo para un subconjunto de clientes.
El canal de incidentes se llenó de la folclore DNS habitual. Alguien vació systemd-resolved en un par de nodos. Otro reinició Docker en un host. Un tercero ejecutó dig en su laptop, obtuvo la nueva IP y declaró victoria.
Mientras tanto, un conjunto de contenedores worker de larga duración seguían hablando con la IP antigua. Ni siquiera estaban haciendo DNS; tenían pools de conexión manteniendo sesiones TLS abiertas por horas.
La pista vino desde dentro del contenedor: ss -tnp mostró conexiones establecidas hacia la dirección antigua. DNS estaba “correcto” y seguía siendo irrelevante.
La solución no fue vaciar cachés. Fue un reinicio controlado de los workers (rolling, con drain de colas) más ajuste del tiempo de vida de las conexiones del cliente.
Las acciones del postmortem fueron aburridas: documentar que “DNS cambió” no es lo mismo que “el tráfico se movió”, añadir un canario que verifique IPs de peer reales y poner el tiempo de vida de conexiones en configuración en lugar de en defaults de código.
Optimización que salió mal: “Vamos a acelerar DNS añadiendo un caché”
Otro equipo tenía un problema legítimo: sus hosts realizaban muchas búsquedas DNS y picos de latencia aparecían en traces de requests.
La solución obvia fue poner un resolvedor caché local en cada nodo. Desplegaron dnsmasq y apuntaron el host a él. Las búsquedas se hicieron más rápidas. Todos celebraron.
Luego vino un incidente que no parecía relacionado: un blue/green deploy cambió un servicio interno a una IP nueva, pero un segmento de contenedores siguió golpeando el backend antiguo mucho más tiempo que el TTL.
El resolvedor caché había sido configurado con TTLs mínimos agresivos “para reducir carga de consultas”. No era malicioso; era “eficiente”.
Desafortunadamente, la eficiencia es cómo creas respuestas obsoletas a escala.
El equipo persiguió primero a Docker, porque los contenedores eran el cambio visible. Pero el problema real fue la política de caché en dnsmasq combinada con caché negativa para un registro que desapareció brevemente durante el corte.
Lo arreglaron alineando el comportamiento de caché con su proceso de cambios real: dejar de sobreescribir TTLs salvo que estén dispuestos a ser pagados por ello, y añadir un paso en el runbook para validar respuestas del upstream durante migraciones.
Lección: cachear es una característica de rendimiento que se vuelve una característica de fiabilidad solo cuando se configura como si fueras a pagarte por ello. De lo contrario es una bomba de tiempo con mecha configurable.
Aburrido pero correcto: la práctica que salvó el día
Una empresa financiera tenía hosts Docker en dos redes: LAN corporativa y un segmento PCI restringido.
El DNS era con horizonte dividido: el mismo nombre podía resolverse distinto según desde dónde se consultara. Esto era intencional, auditado y molesto.
Su equipo SRE tenía la costumbre que parecía burocracia: cada imagen de contenedor incluía una pequeña caja de herramientas de diagnóstico y cada runbook de on-call empezaba con “imprime resolv.conf y consulta el resolvedor exacto mostrado”.
La gente puso los ojos en blanco—hasta una interrupción un jueves por la noche.
Un despliegue en el segmento PCI falló al alcanzar un nombre de servicio interno. Desde laptops funcionaba. Desde algunos hosts funcionaba. Desde otros no.
El runbook separó rápido el problema: los contenedores usaban el DNS embebido de Docker que reenviaba a un resolutor corporativo que no tenía la vista PCI.
Alguien había “estandarizado” los servidores DNS en daemon.json a través de entornos a principios de semana.
Porque tenían la ruta del resolvedor documentada y los pasos de diagnóstico consistentes, revirtieron el cambio en daemon.json en minutos, reiniciaron Docker en un subconjunto pequeño, validaron y luego aplicaron el arreglo de forma segura.
Sin heroísmos. Sin capturas de paquetes a medianoche. Solo observación disciplinada y repetible.
Broma #2: Añadir un caché DNS para arreglar fiabilidad es como añadir un segundo frigorífico para arreglar el hambre—solo almacenarás más malas decisiones para después.
Errores comunes: síntoma → causa raíz → solución
1) Síntoma: “El contenedor resuelve IP antigua, el host resuelve IP nueva”
Causa raíz: El contenedor usa el DNS embebido de Docker que reenvía a resolutores upstream distintos (daemon.json fijado, o Docker eligió servidores diferentes al host).
Solución: Compara el DNS de docker info con el resolvedor del host; consulta directamente los resolutores upstream de Docker; actualiza la configuración DNS del daemon de Docker y reinicia Docker de forma controlada.
2) Síntoma: “Vaciando caché del host no cambió nada”
Causa raíz: La aplicación cachea DNS o mantiene conexiones persistentes; nunca vuelve a consultar.
Solución: Reinicia o recarga la app; configura tiempo máximo de vida de conexiones; reduce keepalive donde sea seguro; verifica con ss que la IP de peer cambie.
3) Síntoma: “DNS funciona en host, el contenedor obtiene timeouts”
Causa raíz: El contenedor tiene nameserver 127.0.0.53 copiado del host, pero eso no es alcanzable dentro del namespace del contenedor.
Solución: Configura Docker para usar resolutores upstream reales (no el stub del host), o asegúrate de que Docker use el resolv.conf no-stub; recrea contenedores.
4) Síntoma: “Un nombre específico siempre resuelve ‘mal’ dentro del contenedor”
Causa raíz: Entrada en /etc/hosts en la imagen o inyectada en tiempo de ejecución; NSS comprueba files antes que DNS.
Solución: Elimina la entrada en hosts; arregla build/entrypoint; valida el orden hosts: files dns y su contenido.
5) Síntoma: “NXDOMAIN intermitente después de crear un registro”
Causa raíz: Caché negativa (SOA minimum/negative TTL) en resolutores upstream o cachés locales.
Solución: Inspecciona SOA negative TTL; espera o consulta autoritativos; evita patrones de borrar-y-recrear durante migraciones.
6) Síntoma: “DNS es lento solo en contenedores”
Causa raíz: Dominios de búsqueda y ndots causando múltiples consultas; o un resolutor upstream alcanzable desde el host pero no desde la red del contenedor por reglas de firewall.
Solución: Inspecciona opciones en /etc/resolv.conf; recorta dominios de búsqueda; asegúrate del camino UDP/TCP 53 desde el namespace del contenedor; mide con dig temporizado.
7) Síntoma: “Nombres de servicios de Docker Compose resuelven de forma inconsistente”
Causa raíz: Múltiples redes, aliases o contenedores obsoletos; el DNS embebido responde distinto según la conexión de red.
Solución: Inspecciona attachments de red; asegura que los servicios compartan la red prevista; elimina contenedores huérfanos y recrea la red del proyecto.
8) Síntoma: “Después de cambiar el DNS corporativo, algunos hosts nunca lo adoptan”
Causa raíz: El daemon de Docker tiene servidores DNS fijados; los contenedores siguen usando el DNS embebido que reenvía a servidores antiguos.
Solución: Actualiza daemon.json vía gestión de configuración; reinicia Docker en una ventana de mantenimiento; redepliega contenedores creados bajo la configuración vieja.
Listas de verificación / plan paso a paso (aburrido a propósito)
Paso a paso: aisla al mentiroso en 15 minutos
- Elige un contenedor fallido y uno sano (si tienes uno sano). Preferible misma imagen.
-
Registra la configuración del resolvedor del contenedor:
cat /etc/resolv.conf,cat /etc/nsswitch.conf, ycat /etc/hosts. - Consulta el nombre con una herramienta que muestre TTL (dig) desde dentro del contenedor.
- Consulta el mismo nombre desde el host y contra los resolutores upstream específicos que usa Docker.
- Si las respuestas DNS difieren: identifica la primera capa donde divergen (Docker upstream vs host upstream vs DNS con horizonte dividido).
- Si las respuestas coinciden pero la app sigue usando la IP antigua: inspecciona conexiones TCP existentes, ajustes de pool y caché de la aplicación.
- Aplica el cambio mínimo que fuerce una ruta de resolución nueva: vacía la caché correcta o reinicia el proceso adecuado.
-
Valida con evidencia: muestra la nueva respuesta DNS y muestra la nueva IP de peer con
sso logs de requests.
Checklist de despliegue: no crees sorpresas DNS
- No incluyas hacks en
/etc/hostsen imágenes a menos que también incluyas un plan para removarlos. - Mantén la configuración DNS del daemon de Docker explícita por entorno; no asumas que el DNS del host es igual al del contenedor.
- Para servicios detrás de balanceo por DNS, ajusta el tiempo de vida de las conexiones del cliente para que el tráfico realmente pueda moverse.
- Durante migraciones, evita eventos transitorios de NXDOMAIN; la caché negativa los hará persistir.
- Tener un contenedor diagnóstico canónico (o toolbox) disponible en cada host/cluster.
Plan de cambio: actualizar DNS de Docker de forma segura en una flota de hosts
- Identifica qué hosts usan fuertemente DNS embebido de Docker (redes definidas por usuario, stacks de Compose).
- Actualiza
/etc/docker/daemon.jsoncon servidoresdnscorrectos y (si es necesario) opciones. - Programa un reinicio del daemon de Docker; entiende el impacto (los contenedores pueden reiniciar según la política).
- Reinicia Docker en un host canario primero; valida
/etc/resolv.confen contenedores y resultados dedig. - Rueda por la flota; recrea contenedores que fueron creados con la configuración DNS vieja si el comportamiento persiste.
Preguntas frecuentes
1) ¿Por qué docker exec muestra nameserver 127.0.0.11?
Ese es el DNS embebido de Docker para redes definidas por el usuario. Proporciona resolución de nombres de contenedores/servicios y reenvía otras consultas upstream.
Es normal, y también es la razón por la que “vaciar la caché DNS del host” a menudo no hace lo que esperas.
2) ¿Puedo vaciar directamente la caché DNS embebida de Docker?
No de forma limpia con “un comando” como systemd-resolved. Operativamente, la influencias cambiando resolutores upstream, recreando contenedores/redes o reiniciando Docker.
Antes de hacerlo, demuestra que la capa embebida es donde empieza la divergencia comparando respuestas entre capas.
3) ¿Por qué reiniciar el contenedor a veces arregla DNS?
Puede cambiar varias cosas a la vez: el contenedor obtiene un /etc/resolv.conf fresco, la aplicación reinicia y descarta cachés/pools internos, y Docker puede reconstruir partes del estado de red.
Es un instrumento contundente. Útil, pero oculta la causa raíz si lo haces primero.
4) Mi TTL es 30 segundos. ¿Por qué las respuestas obsoletas persistieron minutos?
Porque el TTL aplica a cachés DNS, no a las conexiones existentes de tu aplicación, y no necesariamente al cacheo a nivel de aplicación.
Además, los resolutores upstream pueden limitar TTLs o imponer TTLs mínimos, y la caché negativa tiene sus propios temporizadores.
5) ¿Por qué el host resuelve correctamente pero los contenedores no después de activar systemd-resolved?
Si los contenedores acaban con nameserver 127.0.0.53, eso apunta a sí mismos, no al stub del host.
Configura Docker para usar resolutores upstream reales o una IP de resolvedor alcanzable, no el stub loopback del host.
6) ¿Debería ejecutar un caché DNS dentro de cada contenedor?
No, a menos que disfrutes depurar múltiples capas de caché bajo presión de incidentes.
Si necesitas caché, hazlo a nivel de nodo (propio y observable) o mediante una capa de resolutor dedicada, y mantén las rutas de resolvedor de los contenedores simples.
7) ¿Cuál es la forma más rápida de demostrar que no es DNS en absoluto?
Muestra que dig devuelve la IP nueva, pero ss -tnp muestra conexiones establecidas a la IP antigua, o los logs de requests muestran el peer antiguo.
Eso es territorio de pooles de conexiones / keepalive / caché de la app.
8) ¿Kubernetes cambia esta historia?
Sí, pero la forma es similar: los contenedores suelen consultar CoreDNS, que cachea y reenvía. Siguen existiendo caché de la aplicación, comportamiento de libc y comportamiento de resolutores upstream.
La diferencia principal es que el “DNS embebido” típicamente es CoreDNS más convenciones kube-dns, no el 127.0.0.11 de Docker.
9) ¿Por qué nslookup de BusyBox discrepa con dig?
Herramientas distintas tienen comportamientos de resolvedor distintos, salidas diferentes y a veces defaults distintos (TCP vs UDP, comportamiento de search, reintentos).
Prefiere dig para claridad (TTL, secciones authority/additional) y usa ejecuciones temporizadas para detectar reintentos.
10) ¿Cuándo es incorrecto vaciar cachés?
Cuando no has demostrado que el caché es el problema. Los timeouts por firewall, MTU o IP de resolvedor inalcanzable no se arreglan vaciando cachés.
Además, vaciar cachés upstream puede crear una estampida si muchos nodos vuelven a consultar a la vez.
Conclusión: próximos pasos que puedes tomar
Los problemas de DNS en Docker rara vez son misteriosos. Son por capas. La mentira suele vivir en tres lugares:
la aplicación que nunca vuelve a resolver, la ruta de resolvedor que difiere entre host y contenedor, o una caché upstream que está haciendo exactamente lo que se configuró para hacer.
La próxima vez que veas “DNS obsoleto”:
- Empieza imprimiendo
/etc/resolv.confdentro del contenedor y deja de adivinar. - Haz
diglado a lado desde contenedor y host, y luego directamente contra los resolutores upstream de Docker. - Si el DNS es correcto pero el tráfico está mal, inspecciona conexiones establecidas y reinicia el proceso correcto, no el universo.
- Estandariza una pequeña caja de herramientas diagnóstica y un runbook que obligue a recolectar evidencia antes de vaciar cualquier cosa.
Si haces esas cuatro cosas consistentemente, las “mentiras del caché DNS” se vuelven una molestia manejable en lugar de un tema recurrente de incidentes.