Todo “funciona” hasta que deja de hacerlo. Los pings pequeños tienen éxito, las comprobaciones de estado están en verde,
las páginas de inicio de sesión cargan, y luego aparece una petición real: un POST JSON de 2 MB, un handshake TLS con una
cadena de certificados pesada, un stream gRPC, o un contenedor tirando una capa. De repente: bloqueos, timeouts,
reintentos misteriosos, o ese clásico mensaje corporativo en Slack: “¿La red está lenta?”
Si esto solo ocurre en Docker (o “solo desde contenedores”, o “solo a través de la VPN”, o “solo cuando el tráfico pasa por el
overlay”), probablemente estés frente a un problema de MTU/MSS. Los fallos de MTU hacen que gente experta dude de la realidad.
Eso ocurre porque los síntomas son selectivos, la observabilidad suele ser pobre por defecto, y las correcciones son
peligrosamente fáciles de hacer mal.
MTU, MSS, fragmentación, PMTUD: qué se rompe realmente
MTU en una frase
MTU (Unidad Máxima de Transmisión) es el tamaño máximo de paquete IP (en bytes) que puede atravesar un enlace sin ser
fragmentado en ese salto.
Y MSS es la pieza que la gente olvida
MSS de TCP (Tamaño Máximo de Segmento) es la mayor carga útil TCP que un host pondrá en un único segmento TCP; se deriva
de la MTU del camino menos las cabeceras IP y TCP. En Ethernet/IP/TCP sin opciones, una MTU de 1500 suele dar una MSS de
1460 bytes (1500 – 20 – 20).
MTU trata sobre paquetes en la red. MSS trata sobre el tamaño de los trozos que TCP envía. Si reduces (clamp) la MSS, puedes
evitar la fragmentación asegurando que los paquetes sean lo bastante pequeños para caber en la MTU más pequeña del camino.
Por qué las solicitudes pequeñas funcionan y las grandes fallan
Con una discrepancia de MTU puedes obtener un “fallo selectivo”:
- Las cargas pequeñas caben en paquetes pequeños; pasan.
- Las cargas grandes generan paquetes que exceden la MTU de algún salto; necesitan fragmentación o una MSS menor.
- Si la fragmentación está bloqueada o la Detección de MTU de Ruta (PMTUD) está rota, esos paquetes grandes desaparecen en la nada.
Fragmentación, DF y el patrón de agujero negro
IPv4 puede fragmentar paquetes en tránsito. Pero las pilas modernas evitan fragmentar cuando pueden. Ponen el bit DF (Don’t Fragment)
y confían en PMTUD: el emisor envía paquetes con DF y espera que la red le diga la MTU máxima enviando mensajes ICMP “Fragmentation needed”
cuando un paquete es demasiado grande.
Cuando esos mensajes ICMP son filtrados (por grupos de seguridad, cortafuegos, dispositivos “útiles” o políticas mal configuradas),
PMTUD falla silenciosamente. El emisor sigue retransmitiendo paquetes que nunca cabrán. Para la aplicación, esto parece un bloqueo:
la conexión TCP se establece, quizá algunos datos se entregan, y luego todo se queda atascado.
Una cita útil para pegar en una nota:
Idea parafraseada: “Esperanza no es una estrategia.”
— atribuida en círculos SRE a la cultura de operaciones; trata la MTU como
un parámetro de diseño, no como un deseo.
Docker facilita crear discrepancias de MTU
Docker añade capas: bridges, pares veth, NAT, y a veces overlays (VXLAN) o túneles (WireGuard, IPsec, cliente VPN).
Cada capa de encapsulación consume bytes. Come suficientes bytes y tu “1500” se convierte en “1450” o “1412” o “1376” en la práctica.
Si solo algunos nodos o rutas tienen ese overhead, enhorabuena: tienes un agujero negro parcial.
Broma #1: Los bugs de MTU son como las impresoras de oficina — esperan hasta que llegas tarde, y entonces empiezan a mostrar “personalidad”.
Dónde Docker oculta los problemas de MTU
La topología habitual
En un host Linux típico con la red bridge por defecto de Docker:
- Un contenedor se conecta mediante un par
vethadocker0(un bridge de Linux). - El host enruta/NATea el tráfico hacia una NIC física como
eth0oens5. - Desde allí puede atravesar VLANs, tejidos de VPC, VPNs, proxies y otras invenciones mágicas corporativas.
El contenedor ve una MTU en su interfaz (a menudo 1500). El bridge ve una MTU (a menudo 1500). La NIC del host puede ser 1500.
Pero si el camino real incluye encapsulación (p. ej., la VPN añade ~60–80 bytes, VXLAN añade ~50 bytes, GRE añade ~24 bytes,
más posibles cabeceras extra), la MTU efectiva es menor. Si nadie avisa a las pilas TCP, los segmentos grandes serán demasiado grandes.
Las redes overlay lo complican
Docker Swarm overlay y muchos plugins CNI usan VXLAN u otras encapsulaciones para construir semánticas L2 sobre L3. VXLAN suele
añadir 50 bytes de overhead (Ethernet externo + IP externo + UDP + cabeceras VXLAN; el overhead exacto depende del entorno).
Si el underlay es 1500, la MTU del overlay debería estar alrededor de 1450.
Aparecen problemas cuando:
- Algunos nodos configuran la MTU del overlay a 1450 y otros la dejan en 1500.
- El underlay no es realmente 1500 (las nubes varían; las VPN aún más).
- ICMP está filtrado entre nodos, rompiendo PMTUD.
Por qué lo percibes como “TLS es inestable” o “los POSTs se quedan colgados”
TLS puede ser un probador accidental de MTU porque certificados y vuelos de handshake pueden ser más grandes de lo esperado. Lo mismo ocurre
con metadata gRPC, tramas HTTP/2, y JSON “pequeño” que en realidad es enorme cuando alguien inserta un blob base64.
Un síntoma clásico: el handshake TCP se completa, fluyen cabeceras de respuesta pequeñas, y luego la conexión se queda atascada cuando el
primer segmento grande con DF llega a un salto con MTU menor. Se producen retransmisiones. Finalmente obtienes un timeout.
Datos interesantes y un poco de historia
- Dato 1: la MTU de 1500 bytes en Ethernet se impuso por compensaciones de diseño tempranas, no porque 1500 sea sagrada.
- Dato 2: la Detección de MTU de Ruta (PMTUD) se introdujo porque la fragmentación es costosa y frágil; mueve el trabajo a los endpoints.
- Dato 3: PMTUD depende de ICMP. Bloquear todo ICMP es como quitar las señales de tráfico y culpar a los conductores por perderse.
- Dato 4: el overhead de encapsulación VXLAN suele empujar la MTU del overlay a ~1450 sobre un underlay de 1500; si mantienes 1500, apuestas por fragmentación o jumbo frames.
- Dato 5: los jumbo frames (MTU 9000) pueden reducir la carga CPU en algunos casos, pero un único salto con solo 1500 arruina tu día a menos que clamps o segments correctamente.
- Dato 6: los routers IPv6 no fragmentan en tránsito. Si rompes PMTUD en IPv6, no obtienes “a veces”. Obtienes “no funciona”.
- Dato 7: MSS de TCP se negocia por dirección durante SYN/SYN-ACK; los middleboxes pueden (y a menudo deberían) reescribirlo al vuelo para túneles.
- Dato 8: muchos “timeouts aleatorios” achacados a servidores de aplicación son en realidad tormentas de retransmisión por PMTUD en agujero negro.
- Dato 9: el bridge por defecto de Docker y los dispositivos
vethnormalmente heredan la MTU del host al crearse; cambiar la MTU del host después no siempre retrofitea losvethexistentes.
Guía rápida de diagnóstico
Este es el orden que encuentra el culpable más rápido en producción, con la menor cantidad de callejones sin salida.
1) Confirma que depende del tamaño y de la ruta
- Desde un contenedor, ejecuta una petición pequeña y otra grande al mismo destino.
- Si la pequeña funciona y la grande cuelga/tiempo de espera, sospecha de MTU inmediatamente.
2) Encuentra la MTU de camino efectiva con pings DF
- Usa
ping -M do -spara hacer una búsqueda binaria del payload máximo que pasa. - Compara resultados desde el host vs desde el contenedor. Las diferencias importan.
3) Inspecciona valores MTU en todas las interfaces relevantes
- NIC física del host, bridge de Docker,
veth, overlays, interfaz de VPN/túnel. - Si alguna interfaz de túnel tiene 1420 y los contenedores creen tener 1500, probablemente lo encontraste.
4) Valida que PMTUD no esté siendo bloqueada
- Revisa reglas de firewall para ICMP tipo 3 código 4 (IPv4) y ICMPv6 Packet Too Big (tipo 2).
- Captura tráfico: ¿están volviendo errores ICMP? ¿Hay retransmisiones repetidas?
5) Elige tu estrategia de arreglo
- Mejor: establece la MTU correcta end-to-end (underlay, overlay, contenedores).
- Pragmático: haz clamp de MSS en salida/entrada del túnel o del bridge.
- Evitar: “bajar todo a ciegas” sin entender; enmascararás el problema y podrías reducir el rendimiento.
Tareas prácticas: comandos, salida esperada y decisiones
Estas son tareas de grado producción: qué ejecutas, qué esperas ver, qué significa cuando no lo ves y qué haces después. Ejecútalas
en un host Linux con Docker salvo indicación contraria.
Task 1: Reproducir desde dentro de un contenedor con payload pequeño vs grande
cr0x@server:~$ docker run --rm curlimages/curl:8.6.0 curl -sS -o /dev/null -w "%{http_code}\n" http://10.20.30.40:8080/health
200
Significado: la conectividad básica funciona.
Decisión: ahora prueba una petición grande que provoque segmentos TCP grandes.
cr0x@server:~$ docker run --rm curlimages/curl:8.6.0 sh -lc 'dd if=/dev/zero bs=1k count=2048 2>/dev/null | curl -sS -m 10 -o /dev/null -w "%{http_code}\n" -X POST --data-binary @- http://10.20.30.40:8080/upload'
curl: (28) Operation timed out after 10000 milliseconds with 0 bytes received
Significado: hay un patrón de fallo dependiente del tamaño.
Decisión: deja de culpar a la aplicación hasta probar que los paquetes pueden atravesar el camino.
Task 2: Comparar comportamiento host vs contenedor
cr0x@server:~$ dd if=/dev/zero bs=1k count=2048 2>/dev/null | curl -sS -m 10 -o /dev/null -w "%{http_code}\n" -X POST --data-binary @- http://10.20.30.40:8080/upload
200
Significado: el camino desde el host funciona pero el camino desde el contenedor falla.
Decisión: enfócate en bridge/veth/iptables de Docker y la herencia de MTU en lugar de solo la red upstream.
Task 3: Revisar MTU en NIC del host y bridge de Docker
cr0x@server:~$ ip -br link show dev eth0
eth0 UP 10.0.0.15/24 fe80::a00:27ff:feaa:bbbb/64 mtu 1500
cr0x@server:~$ ip -br link show dev docker0
docker0 UP 172.17.0.1/16 fe80::42:1cff:fe11:2222/64 mtu 1500
Significado: ambos están a 1500, lo “normal” pero no necesariamente correcto para tu camino real.
Decisión: identifica túneles/overlays. Si están presentes, 1500 puede ser engañoso.
Task 4: Inspeccionar MTU dentro de un contenedor en ejecución
cr0x@server:~$ docker run --rm --network bridge alpine:3.19 ip -br link show dev eth0
eth0@if8 UP 172.17.0.2/16 fe80::42:acff:fe11:0002/64 mtu 1500
Significado: el contenedor cree que su MTU es 1500.
Decisión: si el camino contiene encapsulación (VPN/VXLAN), probablemente necesites una MTU menor en el contenedor o clamp de MSS.
Task 5: Buscar interfaces de túnel y su MTU
cr0x@server:~$ ip -br link | egrep 'wg0|tun0|tap|vxlan|geneve|gre' || true
wg0 UP 10.8.0.2/24 fe80::aaaa:bbbb:cccc:dddd/64 mtu 1420
Significado: WireGuard MTU 1420 es una pista fuerte. Si el tráfico del contenedor sale por wg0, los paquetes de 1500 no cabrán.
Decisión: o bajas la MTU en contenedor/bridge para igualar la MTU más pequeña de salida, o aplicas clamp de MSS al tráfico que sale por wg0.
Task 6: Determinar la MTU efectiva del camino con ping DF (host)
cr0x@server:~$ ping -c 2 -M do -s 1472 10.20.30.40
PING 10.20.30.40 (10.20.30.40) 1472(1500) bytes of data.
ping: local error: message too long, mtu=1420
ping: local error: message too long, mtu=1420
--- 10.20.30.40 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 0ms
Significado: el host ya conoce una MTU 1420 en la ruta/interfaz relevante (probablemente porque el tráfico sale por wg0).
Decisión: ahora prueba tamaños menores para encontrar el máximo que pasa; confirma lo mismo desde un contenedor.
Task 7: Búsqueda binaria de un tamaño DF que funcione (host)
cr0x@server:~$ ping -c 2 -M do -s 1392 10.20.30.40
PING 10.20.30.40 (10.20.30.40) 1392(1420) bytes of data.
1400 bytes from 10.20.30.40: icmp_seq=1 ttl=62 time=12.3 ms
1400 bytes from 10.20.30.40: icmp_seq=2 ttl=62 time=12.1 ms
--- 10.20.30.40 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 12.051/12.173/12.296/0.122 ms
Significado: la MTU del camino está alrededor de 1420 (1392 payload + 28 cabeceras ICMP/IP = 1420).
Decisión: configura MTU/MSS para que los segmentos TCP de carga útil encajen por debajo de ~1420 (prácticamente: MSS alrededor de 1380–1390 dependiendo de cabeceras/opciones).
Task 8: Ejecutar ping DF desde dentro de un contenedor (para detectar desajuste)
cr0x@server:~$ docker run --rm alpine:3.19 sh -lc 'ping -c 2 -M do -s 1472 10.20.30.40'
PING 10.20.30.40 (10.20.30.40): 1472 data bytes
ping: sendto: Message too long
ping: sendto: Message too long
--- 10.20.30.40 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss
Significado: el contenedor no puede enviar paquetes DF tan grandes; o bien ve una MTU menor en su salida, o la pila rechaza según la MTU descubierta.
Decisión: inspecciona el enrutamiento desde el namespace del contenedor y confirma por qué interfaz sale el tráfico en el host.
Task 9: Identificar el veth del contenedor y comprobar su MTU en el host
cr0x@server:~$ cid=$(docker run -d alpine:3.19 sleep 300); echo "$cid"
b2e1e3d4c5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2
cr0x@server:~$ pid=$(docker inspect -f '{{.State.Pid}}' "$cid"); echo "$pid"
24188
cr0x@server:~$ nsenter -t "$pid" -n ip -br link show dev eth0
eth0@if10 UP 172.17.0.2/16 fe80::42:acff:fe11:0002/64 mtu 1500
cr0x@server:~$ ifindex=$(nsenter -t "$pid" -n cat /sys/class/net/eth0/iflink); echo "$ifindex"
10
cr0x@server:~$ ip -br link | awk '$1 ~ /^veth/ {print}'
veth1a2b3c4d@if9 UP mtu 1500
Significado: veth está a 1500. Si el tráfico sale por un túnel de MTU 1420, los paquetes pueden ser demasiado grandes a menos que PMTUD funcione.
Decisión: decide si bajar la MTU en bridge/veth o aplicar clamp de MSS.
cr0x@server:~$ docker rm -f "$cid" >/dev/null
Task 10: Comprobar si se están descartando ICMP “Fragmentation needed”
cr0x@server:~$ sudo iptables -S | egrep 'icmp|RELATED|ESTABLISHED'
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
Significado: ICMP está permitido en INPUT aquí; buena señal para PMTUD.
Decisión: revisa también FORWARD (Docker usa forwarding) y cualquier cadena de gestor de firewall.
cr0x@server:~$ sudo iptables -S FORWARD
-P FORWARD DROP
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
Significado: FORWARD con política DROP por defecto está bien si hay excepciones, pero los errores ICMP son “RELATED” y deben pasar. Si no tienes la regla RELATED, PMTUD se rompe.
Decisión: asegúrate de que RELATED,ESTABLISHED esté presente para la dirección relevante, o permite explícitamente tipos ICMP.
Task 11: Observar retransmisiones y MSS con tcpdump
cr0x@server:~$ sudo tcpdump -ni any 'host 10.20.30.40 and tcp' -c 10
tcpdump: data link type LINUX_SLL2
12:01:10.100001 IP 172.17.0.2.51234 > 10.20.30.40.8080: Flags [S], seq 1234567890, win 64240, options [mss 1460,sackOK,TS val 111 ecr 0,nop,wscale 7], length 0
12:01:10.102300 IP 10.20.30.40.8080 > 172.17.0.2.51234: Flags [S.], seq 2222222, ack 1234567891, win 65160, options [mss 1460,sackOK,TS val 222 ecr 111,nop,wscale 7], length 0
12:01:10.103000 IP 172.17.0.2.51234 > 10.20.30.40.8080: Flags [.], ack 1, win 502, options [nop,nop,TS val 112 ecr 222], length 0
12:01:11.105500 IP 172.17.0.2.51234 > 10.20.30.40.8080: Flags [P.], seq 1:1461, ack 1, win 502, options [nop,nop,TS val 113 ecr 222], length 1460
12:01:12.108000 IP 172.17.0.2.51234 > 10.20.30.40.8080: Flags [P.], seq 1:1461, ack 1, win 502, options [nop,nop,TS val 114 ecr 222], length 1460
Significado: MSS es 1460. Segmentos de datos de 1460 bytes se retransmiten, lo que sugiere que no pasan. Si la MTU del camino es ~1420, esos segmentos son demasiado grandes una vez encapsulados.
Decisión: clamp de MSS (p. ej., a 1360–1380) o bajar MTU en la red de contenedores para que la negociación de MSS resulte en un valor menor.
Task 12: Comprobar la MTU de ruta y el enrutamiento por política (común con clientes VPN)
cr0x@server:~$ ip route get 10.20.30.40
10.20.30.40 dev wg0 src 10.8.0.2 uid 1000
cache
Significado: el tráfico sale por wg0. Esa es la interfaz de MTU pequeña.
Decisión: aplica clamp de MSS en la salida de wg0 (o en la entrada) para el tráfico reenviado de contenedores, o ajusta la MTU de la red Docker para encajar con wg0.
Task 13: Inspeccionar la configuración de MTU del demonio Docker (si existe)
cr0x@server:~$ cat /etc/docker/daemon.json
{
"log-driver": "journald"
}
Significado: no hay override de MTU configurado.
Decisión: si necesitas una solución estable tras reinicios y reinicios de contenedores, configura la MTU del demonio Docker explícitamente (o configura la MTU de tu CNI/overlay).
Task 14: Revisar sysctls que influyen en el comportamiento PMTUD
cr0x@server:~$ sysctl net.ipv4.ip_no_pmtu_disc net.ipv4.tcp_mtu_probing
net.ipv4.ip_no_pmtu_disc = 0
net.ipv4.tcp_mtu_probing = 0
Significado: PMTUD está habilitada (bien), pero el sondeo TCP de MTU está apagado (por defecto).
Decisión: no “arregles” la MTU habilitando probing globalmente sin entender el alcance; prefiere MTU/MSS correctos.
Soluciones que funcionan: alineación de MTU y clamp de MSS
Estrategia A: Alinear MTU entre underlay, overlays e interfaces de contenedor
Esta es la solución limpia: los paquetes se dimensionan correctamente de forma natural, PMTUD queda como red de seguridad y tus capturas de tráfico serán aburridas.
Requiere más planificación, pero evita soluciones mágicas.
1) Si usas un túnel, acepta su overhead
Si la salida es por wg0 con MTU 1420, puedes establecer la MTU del bridge de Docker a 1420 (o algo menor para
tener margen según el camino). La idea: asegurar que la MTU de la interfaz del contenedor no sea mayor que la MTU efectiva más pequeña del camino.
2) Configurar la MTU del demonio Docker (redes bridge)
Configura Docker para crear redes con una MTU específica. Ejemplo:
cr0x@server:~$ sudo sh -lc 'cat > /etc/docker/daemon.json <<EOF
{
"mtu": 1420,
"log-driver": "journald"
}
EOF'
cr0x@server:~$ sudo systemctl restart docker
Significado: las nuevas interfaces de contenedores creadas por Docker deberían usar MTU 1420.
Decisión: recrea los contenedores afectados (los veth existentes pueden conservar la MTU antigua). Planifica un despliegue controlado.
3) Para redes definidas por el usuario, establece la MTU explícitamente
cr0x@server:~$ docker network create --driver bridge --opt com.docker.network.driver.mtu=1420 appnet
a1b2c3d4e5f6g7h8i9j0
Significado: esta red usará MTU 1420 para su bridge/veth.
Decisión: conecta cargas de trabajo que atraviesen el túnel a esta red; mantiene redes “solo locales” a 1500 si procede.
4) Overlays: calcula la MTU, no la adivines
Para overlays VXLAN sobre un underlay 1500, 1450 es una configuración común. Si el underlay ya está tunelado (VPN), la MTU del overlay puede necesitar ser aún menor. Un stack VXLAN sobre WireGuard puede quedarse sin espacio con rapidez.
El enfoque correcto es empírico: mide la MTU efectiva entre nodos (ping DF) y luego resta los overheads de encapsulación que añades. O, más directo:
establece la MTU del overlay según la MTU más pequeña real del underlay que tengas, no según la del pedido de compra.
Estrategia B: Clampar TCP MSS (rápido, práctico, un poco feo)
El clamp de MSS es la torniquete de urgencia: detiene la hemorragia aunque no hayas corregido la anatomía. Funciona muy bien cuando la MTU del camino es estable pero más pequeña de lo que creen los endpoints,
o cuando no puedes cambiar la MTU de todos los contenedores/redes rápidamente.
Qué hace: reescribe la MSS en paquetes SYN para que los endpoints nunca envíen segmentos TCP demasiado grandes para el camino.
No arregla protocolos basados en UDP directamente, y no corrige IPv6 a menos que también clamps en ip6tables/nft para v6.
1) Clamp de MSS en tráfico reenviado de contenedores que sale por un túnel
Si los contenedores están detrás de NAT en el host y egress via wg0, clamp en la ruta FORWARD.
cr0x@server:~$ sudo iptables -t mangle -A FORWARD -o wg0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
Significado: el kernel ajusta la MSS en base a la PMTU descubierta para esa ruta.
Decisión: si PMTUD está roto (ICMP bloqueado), --clamp-mss-to-pmtu puede no converger; entonces usa una MSS fija.
2) Clamp a una MSS fija cuando PMTUD es poco fiable
cr0x@server:~$ sudo iptables -t mangle -A FORWARD -o wg0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360
Significado: la MSS será 1360 para esos flujos, encajando dentro de una MTU ~1420 con espacio para cabeceras/opciones.
Decisión: elige MSS de forma conservadora. Si es demasiado baja pagas en rendimiento; si es muy alta no arregla el problema.
3) Verificar que el clamp de MSS está activo
cr0x@server:~$ sudo iptables -t mangle -S FORWARD | grep TCPMSS
-A FORWARD -o wg0 -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360
Significado: la regla está presente.
Decisión: ejecuta tcpdump de nuevo y confirma que el SYN anuncia MSS 1360.
4) Confirma con tcpdump que la MSS cambió
cr0x@server:~$ sudo tcpdump -ni wg0 'tcp[tcpflags] & tcp-syn != 0 and host 10.20.30.40' -c 2
12:05:44.000001 IP 10.8.0.2.53312 > 10.20.30.40.8080: Flags [S], seq 1, win 64240, options [mss 1360,sackOK,TS val 333 ecr 0,nop,wscale 7], length 0
12:05:44.002000 IP 10.20.30.40.8080 > 10.8.0.2.53312: Flags [S.], seq 2, ack 2, win 65160, options [mss 1360,sackOK,TS val 444 ecr 333,nop,wscale 7], length 0
Significado: MSS ahora es 1360; deberías dejar de enviar segmentos sobredimensionados.
Decisión: vuelve a ejecutar la reproducción del “POST grande”. Si funciona, tienes la mitigación inmediata.
Estrategia C: Dejar de romper PMTUD (el equipo de seguridad sobrevivirá)
Si controlas los cortafuegos, permite el ICMP necesario para PMTUD:
- IPv4: ICMP tipo 3 código 4 (“Fragmentation needed”).
- IPv6: ICMPv6 tipo 2 (“Packet Too Big”).
También permite tráfico conntrack “RELATED” para flujos reenviados. Los hosts Docker reenvían tráfico; trátalos como routers.
Broma #2: Bloquear ICMP para “mejorar la seguridad” es como quitar la luz de aceite porque distrae.
Tres micro-historias corporativas (todas verosímiles)
Incidente 1: La suposición equivocada (“MTU siempre es 1500”)
Un equipo desplegó un nuevo servicio de ingestión en contenedores. Funcionó perfecto en staging y luego empezó a fallar en producción
solo para ciertos clientes. Las cargas más pequeñas estaban bien. Las subidas grandes se quedaban estancadas y hacían timeout tras unos minutos.
Los logs de aplicación no ayudaban: la petición llegaba y luego nada. Las métricas del balanceador mostraban conexiones colgadas en estado “active”.
El on-call sospechó inicialmente de una dependencia upstream lenta. Razonable, porque los “timeouts” en sistemas distribuidos suelen ser problemas de dependencias.
Pero las capturas mostraron retransmisiones de segmentos TCP de tamaño completo. SYN/SYN-ACK eran normales. El primer trozo del cuerpo desaparecía en el vacío.
La suposición fue simple: “la MTU de la red es 1500.” En la NIC del host lo era. En la ruta hacia ciertos clientes no lo era, porque el tráfico hacía hairpin
por un túnel IPsec gestionado por otro equipo. Ese túnel tenía una MTU efectiva cercana a 1400, y ICMP estaba filtrado “por seguridad”.
La solución fue doble: permitir ICMP relacionado con PMTUD a través de ese túnel y clampear MSS en el borde del túnel. Tras aplicar eso,
las subidas grandes volvieron a la normalidad. La revisión del incidente fue dura pero productiva: no puedes tratar la “MTU” como propiedad de una sola interfaz.
Es una propiedad del camino, y los caminos cambian.
Incidente 2: La optimización que salió mal (“activar jumbo frames en todas partes”)
Otra organización quería mejor rendimiento para transferencias entre contenedores. Alguien propuso activar jumbo frames (MTU 9000) en la red de datos.
Fue presentado como una mejora de bajo riesgo: menos paquetes, menos CPU, mayor rendimiento. Lo probaron entre dos hosts. Fue más rápido. Aplaudieron y fusionaron el cambio.
Una semana después empezaron a ocurrir cosas raras. Algunos servicios estaban bien. Otros mostraban problemas intermitentes: streams gRPC reiniciándose,
fallos ocasionales en handshakes TLS, timeouts “aleatorios” en pulls de imágenes. Lo más confuso: las gráficas de pérdida de paquetes estaban limpias. La latencia era normal.
Solo algunas rutas estaban afectadas.
El problema no era los jumbo frames por sí solos. El problema fue la implantación parcial y un único salto de 1500 bytes en el medio: un cluster de firewalls que no soportaba jumbo frames en una interfaz.
Parte del tráfico pasaba por caminos con jumbo y parte por caminos solo 1500, según enrutamiento y failover.
La red quedó con una MTU dividida. Los hosts emitían tramas de 9000 bytes. Cuando llegaban al salto de 1500 confiaban en PMTUD. ICMP “Packet Too Big” estaba rate-limited en el firewall y a veces se descartaba.
El resultado fue un agujero negro que iba y venía con la carga y eventos de failover.
Finalmente estandarizaron MTU a 1500 en ese segmento y usaron ajuste LRO/GRO junto con paralelismo a nivel de aplicación para el rendimiento. Los jumbo frames pueden ser estupendos,
pero el único MTU jumbo seguro es el que está soportado de forma consistente end-to-end.
Incidente 3: La práctica aburrida que salvó el día (tests MTU estándar en despliegue)
Un equipo de plataforma mantenía un script de “readiness” para cada nodo nuevo. No era glamuroso. Ejecutaba unas comprobaciones: DNS, sincronización horaria, espacio en disco y —calladamente— un par de pruebas PMTU a destinos clave (control plane, registry, gateways del service mesh).
También validaba que las redes de contenedores tuvieran los valores MTU esperados.
Durante una migración, un subconjunto de nodos se aprovisionó en una subred diferente que enrutaba a través de un appliance VPN. Las cargas programadas en esos nodos mostraron inmediatamente mayores tasas de error, pero el script de readiness lo detectó antes de que el impacto fuera mayor.
La prueba PMTU falló para pings DF de 1500 y sugirió una MTU menor.
Al detectarlo temprano, la corrección fue quirúrgica: el equipo configuró la MTU de red Docker en esos nodos para que coincidiera con la del camino VPN y añadió clamp de MSS en la salida de la VPN. También documentaron la diferencia de subred para que networking pudiera eliminar el túnel innecesario más tarde.
Nada de esto fue heroico. Ese es el punto. Las comprobaciones rutinarias de MTU convirtieron un misterio de varios días en un cambio de 30 minutos. Lo aburrido es una característica en operaciones.
Errores comunes: síntoma → causa raíz → solución
1) Síntoma: “Las peticiones pequeñas funcionan; los POST grandes cuelgan desde contenedores”
Causa raíz: desajuste de MTU más PMTUD roto (ICMP bloqueado) o overhead de túnel no tenido en cuenta.
Solución: mide la MTU del camino con ping DF; ajusta la MTU de la red Docker en consecuencia o aplica clamp de MSS en la interfaz de salida.
2) Síntoma: “Funciona en el host, falla en el contenedor”
Causa raíz: el camino de red del contenedor difiere (NAT/reglas de forwarding, tabla de enrutamiento distinta, policy routing, overlay).
Solución: inspecciona ip route get en el host para el destino; tcpdump en docker0 y en la interfaz de salida; clamp de MSS para tráfico reenviado o alinea MTUs.
3) Síntoma: “Handshake TLS falla intermitentemente; curl a veces se queda tras CONNECT”
Causa raíz: registros de handshake TLS grandes exceden la MTU del camino; retransmisiones; ICMP bloqueado o rate-limited.
Solución: clamp de MSS; asegura que ICMP PTB/frag-needed esté permitido; verifica con tcpdump que la MSS se haya reducido.
4) Síntoma: “La red overlay descarta paquetes grandes; ping nodo-a-nodo funciona”
Causa raíz: MTU del overlay no reducida para el overhead de encapsulación (VXLAN/Geneve).
Solución: configura la MTU del overlay/CNI (a menudo ~1450 para VXLAN sobre underlay 1500), garantiza configuración consistente en todos los nodos.
5) Síntoma: “Solo IPv6: algunos destinos inalcanzables, comportamientos raros”
Causa raíz: ICMPv6 Packet Too Big bloqueado; los routers IPv6 no fragmentan, por lo que PMTUD es obligatorio.
Solución: permite ICMPv6 tipo 2; clamp de MSS para TCP sobre IPv6 si es necesario; deja de tratar ICMPv6 como opcional.
6) Síntoma: “Después de cambiar la MTU del host, los contenedores antiguos siguen fallando”
Causa raíz: los veth existentes conservan la MTU antigua; Docker no siempre retrofitea las redes en vivo.
Solución: recrea redes/contenedores; configura la MTU en el demonio o en redes de usuario para que las nuevas conexiones sean correctas.
7) Síntoma: “Solo falla el tráfico por la VPN; el tráfico interno está bien”
Causa raíz: la interfaz VPN tiene MTU menor que la LAN; la MTU del contenedor/bridge es demasiado grande; PMTUD roto a través de la VPN.
Solución: clamp de MSS en la salida de la VPN; opcionalmente usa una red Docker dedicada con MTU menor para cargas hacia la VPN.
8) Síntoma: “Servicio basado en UDP (DNS, QUIC, syslog) descarta mensajes grandes”
Causa raíz: el clamp de MSS no ayuda a UDP; la fragmentación puede estar bloqueada; la carga UDP excede la MTU del camino.
Solución: reduce el tamaño de los mensajes desde la aplicación; usa TCP donde esté soportado; alinea MTU y permite fragmentación/ICMP según corresponda.
Listas de comprobación / plan paso a paso
Checklist A: Contención rápida (15–30 minutos)
- Reproduce el fallo con una petición grande desde dentro de un contenedor.
- Ejecuta ping DF desde el host al destino y encuentra el tamaño máximo que funciona.
- Identifica la interfaz de salida para ese destino (
ip route get). - Aplica clamp de MSS en la interfaz de salida para tráfico reenviado:
- Prefiere
--clamp-mss-to-pmtusi PMTUD funciona. - Usa
--set-msssi PMTUD está roto o es inconsistente.
- Prefiere
- Verifica con tcpdump que la MSS en SYN se redujo.
- Vuelve a ejecutar la prueba de la petición grande. Confirma éxito.
Checklist B: Arreglo correcto (mismo día, menos adrenalina)
- Inventaría las capas de encapsulación en el camino (VPN, overlay, nube, balanceadores).
- Estándariza la MTU del underlay en los enlaces que deben ser “la misma red”.
- Configura la MTU del overlay explícitamente (VXLAN/Geneve/IPIP según corresponda) y asegúrala consistente entre nodos.
- Configura la MTU del demonio Docker o la MTU por red para bridges que deban atravesar caminos restringidos.
- Asegura que los requisitos ICMP para PMTUD estén permitidos (IPv4 frag-needed, IPv6 packet-too-big), incluyendo rutas FORWARD.
- Documenta los valores MTU esperados y añade una prueba PMTU automatizada al readiness de nodos.
Checklist C: Validación post-arreglo (no lo saltes)
- Captura un tcpdump corto durante una transferencia grande; confirma que no hay retransmisiones repetidas de segmentos grandes.
- Revisa las tasas de error de la aplicación; confirma que el síntoma específico desaparece (no solo que “parece mejor”).
- Verifica que el rendimiento sea aceptable; una MSS demasiado pequeña puede reducir el throughput en enlaces con alta BDP.
- Reinicia un nodo (o reinicia Docker) en ventana de mantenimiento para asegurar que la configuración persiste.
Preguntas frecuentes
1) ¿Por qué los pings funcionan pero las subidas HTTP fallan?
Los pings por defecto usan payloads pequeños que caben bajo casi cualquier MTU. Las subidas HTTP grandes generan segmentos TCP grandes
que exceden la MTU del camino y quedan en agujero negro cuando PMTUD/ICMP está roto.
2) ¿Es esto un bug de Docker?
Normalmente no. Docker magnifica la situación: introduce saltos extra (bridge, veth, NAT) y facilita que el tráfico pase por túneles/overlays
con MTU menor de la que asumen los contenedores.
3) ¿Debería poner MTU 1400 en todas partes y listo?
Solo si te apetece pagar una penalización permanente de rendimiento y sufrir regresiones misteriosas más adelante. Mide la MTU mínima real
que necesitas soportar y ajusta la MTU por red. Usa clamp de MSS como mitigación dirigida, no como estilo de vida.
4) ¿Qué es mejor: bajar MTU o clamp de MSS?
Bajar la MTU es más limpio y funciona para TCP y UDP. El clamp de MSS es más rápido de desplegar y suele ser suficiente para TCP,
pero no arregla problemas de tamaño de payload UDP.
5) ¿Por qué bloquear ICMP rompe TCP? TCP no es ICMP.
PMTUD usa ICMP como plano de control para indicar “tu paquete es muy grande”. Sin ese feedback, TCP sigue enviando paquetes DF sobredimensionados
y retransmitiendo infinitamente. El plano de datos espera una señal que nunca llega.
6) ¿Kubernetes cambia algo de esto?
Los conceptos son idénticos; la superficie aumenta. Plugins CNI, encapsulación nodo-a-nodo y service meshes añaden overhead y complejidad.
Kubernetes solo da más lugares donde esconder tu bug de MTU.
7) ¿Y IPv6?
IPv6 depende aún más de PMTUD: los routers no fragmentan. Si ICMPv6 Packet Too Big está bloqueado, verás fallos duros en caminos con MTU menor.
Trata ICMPv6 como infraestructura requerida, no ruido opcional.
8) ¿Pueden los sysctls de sondeo de MTU de TCP arreglarlo?
A veces, pero es un último recurso. El sondeo de MTU puede enmascarar redes rotas adaptándose tras pérdida, pero no sustituye una MTU correcta y ICMP funcionando.
En producción, prefiere soluciones deterministas.
9) ¿Cómo elijo un valor MSS fijo?
Parte de la MTU medida del camino. MSS debería ser MTU menos cabeceras. Para TCP/IPv4 sin opciones suele ser MTU-40, pero las opciones (timestamps, etc.)
reducen efectivamente ese espacio. Por eso valores como 1360 para un camino de 1420 son comunes: conservadores, seguros y no máximos.
10) ¿Por qué falla solo para algunos destinos?
Porque la MTU es una propiedad del camino y las rutas difieren. Un destino puede quedarse en tu LAN a 1500; otro atraviesa un túnel a 1420; un tercero pasa por un firewall mal configurado que descarta ICMP.
Mismo código, distinta física.
Conclusión: próximos pasos prácticos
Cuando las peticiones grandes fallan desde contenedores y todo lo demás parece “bien”, trata MTU/MSS como sospechoso principal. No es superstición; es un modo de fallo conocido con
huellas consistentes: bloqueos dependientes del tamaño, retransmisiones y un camino que silenciosamente no puede llevar lo que envías.
Haz esto a continuación:
- Reproduce con una carga grande desde un contenedor y desde el host. Confirma la forma del fallo.
- Mide la MTU del camino con pings DF hacia el destino real.
- Encuentra la interfaz de salida real y cualquier túnel/overlay en el camino.
- Mitiga inmediatamente con clamp de MSS si necesitas disponibilidad ahora.
- Arregla correctamente alineando MTU entre redes Docker, overlays y underlays, y permitiendo el ICMP requerido por PMTUD.
- Hazlo aburrido para siempre: añade chequeos PMTU al readiness de nodos y estandariza MTU en código.