Guía9 min

DNS del contenedor del agente: 127.0.0.11 y el API de Telegram

Resumen

Cómo no dejar el bot sordo a api.telegram.org: DNS de Compose, 127.0.0.11 y dns: 1.1.1.1 con cuidado. Distinto de extra_hosts y del default bridge. Docker Compose dns, 3 de septiembre de 2026. El resolver interno resuelve postgres; el público, el webhook.

Docker
Resolver interno de Compose y DNS público para APIs del agente

Qué resuelve

Esta pieza se queda en la decisión práctica: qué instalar, qué riesgo agrega y cómo aplicarlo sin romper operación.

El agente resuelve postgres por el DNS interno (127.0.0.11). Si pones dns: 8.8.8.8 solo, postgres deja de existir. Distinto de networks: eso es L2/L3; esto es quién responde A/AAAA. Fuentes oficiales consultadas el 3 de septiembre de 2026.

La regla

No pises el resolver de Compose salvo que sepas el fallback. Default: interno primero, forward al DNS del host.

Si el host tiene DNS roto, el bot no habla con Telegram. Arregla el host o dns: además de no romper nombres de servicio.

extra_hosts es un /etc/hosts estático. Úsalo para un IP fijo de emergencia, no para postgres.

Relación con redes

Una red internal: true no sale a Internet: DNS público no salva. red privada vs public webhook.

IPv6 AAAA que Node no bindea: falla intermitente. Fuerza v4 si el VPS es un desastre.

Tabla

DNS interno versus público

NombreQuiénSi pones solo 1.1.1.1
postgres127.0.0.11NXDOMAIN
api.telegram.orgforwardok
agent (otro svc)127.0.0.11NXDOMAIN

Errores comunes

postgres NXDOMAIN

SíntomaCausaFix
ECONNREFUSED postgresdns público soloquita dns: o dual
getaddrinfo ENOTFOUND telegramhost DNSdns 1.1.1.1 sin quitar interno
timeout 30sIPv6NODE_OPTIONS ipv4
extra_hosts postgresIP cambióDNS de Compose
Fly 6PNcopy yamlDNS de Fly

Relación con el resto

Checklist

  • getent hosts postgres dentro del agent
  • getent hosts api.telegram.org
  • no dns: solo-público
  • extra_hosts justificado
  • Ensayo: compose exec agent node -e "dns.lookup('postgres')"
  • sin AAAA roto

FAQ

¿Workers? DNS de Cloudflare, no 127.0.0.11.

¿Vercel? Runtime de plataforma.

¿network_mode: host? DNS del host; pierdes nombres de servicio.

El ensayo: rompe a propósito con dns: ["1.1.1.1"]; postgres falla. Revert. Telegram lookup ok en default.

Un dns_search raro no sustituye el nombre corto postgres.

Qué no hacer

No copies dns: 8.8.8.8 de un gist de WordPress. El agente necesita nombres de Compose. No uses network_mode: host “para que DNS funcione”: publicas puertos del host.

TTL agresivo: Telegram rota IPs. Cache eterno en un sidecar DNS casero = 3am outage.

Techos oficiales (curl 2026-09-03)

Compose dns (spec de servicios, hoy): define servidores DNS custom en la interfaz de red del contenedor. Un valor o una lista. Ejemplos oficiales: dns: 8.8.8.8 y dns: [8.8.8.8, 9.9.9.9]. No dice “se suma al 127.0.0.11”: pisa la config de la interfaz. Por eso un gist con solo 8.8.8.8 mata postgres.

dns_opt (hoy): opciones del resolver escritas en /etc/resolv.conf (Linux). Ejemplo oficial: use-vc y no-tld-query. No es un segundo nameserver.

dns_search (hoy): dominios de búsqueda en la interfaz. Un valor o lista (example.com, o dc1.example.com + dc2.example.com). Sirve para FQDN cortos de un AD interno. No registra el nombre de servicio postgres.

domainname / hostname (hoy): deben ser un hostname RFC 1123. hostname es el nombre del contenedor, no un alias de otro servicio.

Engine networking (hoy):

  • Por defecto el contenedor hereda el DNS del host (/etc/resolv.conf).
  • En el default bridge recibe una copia de ese archivo. En un custom network (el de Compose) usa el DNS embebido de Docker.
  • Dirección del embebido: 127.0.0.11. No hay equivalente IPv6; esa IPv4 funciona también en contenedores solo-IPv6. Si una lib exige IP explícita del resolver, usa 127.0.0.11, no 8.8.8.8.
  • Varios --dns en el default bridge: lo decide la librería resolver del proceso. Unas preguntan en orden; otras en paralelo y se quedan con la primera respuesta, incluso NXDOMAIN. En custom network el embebido pregunta upstream en orden y para tras éxito o NXDOMAIN.
  • --dns=127.0.0.1 es el loopback del contenedor, no el del host. Los requests salen del netns del container.
  • --dns-search: busca hostnames no FQDN. --dns-opt: par clave-valor de resolv.conf. --hostname default = ID del contenedor.
  • Los custom hosts del /etc/hosts del host no se heredan.

Bridge user-defined (hoy): resolución automática por nombre entre contenedores. En el default bridge solo por IP, salvo --link (legacy). Compose crea <proyecto>_default (directorio, o --project-name / COMPOSE_PROJECT_NAME) y cada servicio registra su nombre. Tráfico servicio-a-servicio usa el puerto del contenedor, no el HOST_PORT.

extra_hosts vs DNS

Compose extra_hosts (hoy): añade mappings a la config de red (/etc/hosts en Linux).

  • Sintaxis corta: lista HOSTNAME=IP. = preferido; : también, desde Compose 2.24.1. IPv6 entre corchetes: "myhostv6=[::1]".
  • Sintaxis larga: mapping hostname → IP.
  • Ejemplo oficial termina en /etc/hosts como 162.242.195.82 somehost.

CLI --add-host (hoy): host:ip. Valor especial host-gateway: IP interna del host. Convención: host.docker.internal=host-gateway. Docker Desktop resuelve host.docker.internal solo; en un VPS Linux hay que poner el --add-host (o el yaml). No es un contrato de CDN.

extra_hosts: ["api.telegram.org:1.2.3.4"] se pudre. IP de CDN no es contrato. Solo para un bastion interno con IP reservada.

host.docker.internal en Mac no es prod Linux. En VPS usa el gateway de Compose o un servicio.

Node: lookup no es resolve

dns.lookup() usa las facilidades del OS (el resolv.conf del contenedor). dns.resolve*() habla DNS “de verdad”. dns.setServers() no cambia lookup(); solo resolve* / reverse. No lo llames a mitad de un query. Los fallbacks del array solo entran si el anterior timeout u otro error: un NOTFOUND no prueba el siguiente (como resolv.conf).

dns.setDefaultResultOrder(order) (añadido 16.4 / 14.18; default cambió a verbatim en 17.0): 'ipv4first' | 'ipv6first' | 'verbatim'. ipv6first desde 22.1 / 20.13. Gana a --dns-result-order. En worker threads el set del main no pisa a los workers. verbatim en lookup() está deprecado a favor de order. Si el VPS entrega AAAA rota, ipv4first es el knob; no un cache de 24 h.

Firewall

UFW del VPS que bloquea 53 UDP hacia el host rompe el forward del 127.0.0.11. El síntoma es “telegram timeout” con postgres ok (interno no sale). Abre 53 al docker0 o no filtres el bridge.

secrets no van por DNS. Un token mal leído no es ENOTFOUND.

pull_policy no cambia DNS. Un recreate sí regenera resolv.conf.

Cómo verificar:

docker compose exec agent cat /etc/resolv.conf
docker compose exec agent getent hosts postgres
docker compose exec agent getent hosts api.telegram.org

nameserver 127.0.0.11 = custom network. Si ves solo 1.1.1.1, revert el dns:.

Siguiente paso: si DNS ok y no conecta, networks y ports. Sin runtime: curso.