WireGuard está lento: MTU, enrutamiento, CPU — Acelérelo sin adivinanzas

¿Te fue útil?

WireGuard “funciona”, que es el estado más peligroso en el que puede estar una VPN. Puedes hacer ping, SSH, quizá incluso abrir una página web—y sin embargo las transferencias de archivos van a paso de tortuga,
las copias de seguridad pierden su ventana, y tu fibra “rápida” parece la Wi‑Fi de un motel con problemas de compromiso.

Esta es la guía de campo para convertir “WireGuard está lento” en un cuello de botella medido, una corrección específica y un método repetible. Sin folklore. Sin
números de MTU sacados de un post de foro de 2019. Vamos a probar, leer los contadores y hacer cambios que puedas defender en un postmortem.

El modelo mental: dónde WireGuard puede ir lento

WireGuard es “solo” una interfaz de red y un transporte UDP. Esa simplicidad es la razón por la que es tan querido—y también por la que los problemas de rendimiento suelen venir
de todo lo que lo rodea: MTU, enrutamiento, NAT, comportamiento del driver de la NIC, colas del kernel y planificación de CPU.

Hay cuatro modos de fallo comunes que se presentan como “lento”:

  • Path MTU / agujeros por fragmentación: los paquetes pequeños funcionan, las grandes transferencias se estancan u oscilan.
  • Ruta equivocada / política equivocada: el tráfico hace hairpin, atraviesa NAT dos veces o sale por la interfaz equivocada.
  • Cuello de botella de CPU: un núcleo se queda al 100% durante iperf; el throughput se plafona en un valor sospechosamente redondo.
  • Pérdida/acolchonamiento en UDP: TCP sobre UDP reacciona mal cuando el underlay descarta o reordena paquetes.

Quieres evitar “tunear” hasta saber cuál tienes. El tuning a ciegas es cómo acabas con una configuración que solo funciona los martes.

Aquí está la lente operativa: empieza con un único flujo reproducible (iperf3 está bien). Confirma si el cuello de botella está en el host local,
host remoto, o en la ruta. Luego aplica el cambio más pequeño que mueva la aguja y mide de nuevo.

Una cita para pegar en un sticky: “La esperanza no es una estrategia.” — Gene Kranz.

Broma #1: Si estás cambiando valores de MTU al azar, no estás afinando una VPN—estás haciendo numerología con pasos extra.

Guion de diagnóstico rápido (primero/segundo/tercero)

Primero: demuestra si es MTU/fragmentación

  1. Ejecuta una prueba ping estilo PMTU (no adivines). Si pings grandes con DF fallan, tienes un agujero o un desajuste.
  2. Revisa contadores de fragmentación necesaria / ICMP bloqueado.
  3. Si PMTU está roto, para y arregla MTU o MSS clamping antes de tocar otra cosa.

Segundo: demuestra si es CPU

  1. Ejecuta iperf3 a través del túnel mientras observas uso por núcleo y carga de softirq.
  2. Si un núcleo se queda fijo (o ksoftirqd se vuelve loco), estás limitado por CPU/interrupts.
  3. Arregla habilitando una ruta de cifrado más rápida (normalmente ya buena), mejorando la distribución NIC/IRQ, o escalando flujos/peers/hosts.

Tercero: demuestra enrutamiento y corrección de la ruta

  1. Verifica la ruta al destino (y la selección de dirección origen) desde el host que envía.
  2. Busca enrutamiento asimétrico: una dirección usa el túnel y la otra usa el WAN.
  3. Confirma que NAT y reglas de firewall no reescriben o limitan UDP.

Si los tres parecen sanos, trátalo como pérdida/cola en UDP

  1. Mide pérdida/retransmisiones vía estadísticas TCP y contadores de interfaz.
  2. Revisa qdisc, shaping, bufferbloat y salud del enlace underlay.
  3. Sólo entonces considera tuning avanzado (buffers de socket, fq, pacing).

Hechos interesantes y contexto (por qué existen estos problemas)

  • WireGuard se incorporó al kernel de Linux en 2020, lo que hizo el rendimiento y el despliegue mucho más predecibles que los módulos fuera del árbol.
  • Rueda sobre UDP por diseño, en parte para evitar el colapso TCP‑sobre‑TCP y en parte por una NAT traversal más simple—sin embargo hereda la realidad “best effort” de UDP.
  • Su criptografía usa ChaCha20-Poly1305, elegido por su buen rendimiento en sistemas sin aceleración AES; en muchas CPU es estremecedoramente rápido.
  • Path MTU Discovery ha sido frágil durante décadas porque depende de mensajes ICMP “fragmentation needed” que a los firewalls les encanta bloquear.
  • El MTU clásico de Ethernet de 1500 es un artefacto histórico, no una ley de la física; los túneles añaden cabeceras y hacen de 1500 una trampa.
  • Los offloads de Linux (GSO/GRO/TSO) pueden hacer que las capturas de paquetes parezcan “erróneas” y también ocultar problemas de rendimiento hasta que una actualización de driver cambia el comportamiento.
  • Los peers de WireGuard se identifican por claves públicas, no por IPs; los errores de enrutamiento a menudo se manifiestan como “se conecta pero está lento” cuando el tráfico coincide con AllowedIPs equivocadas.
  • Las redes en la nube frecuentemente encapsulan ya tus paquetes (VXLAN/Geneve), por lo que tu túnel puede ser un túnel dentro de otro túnel—muerte por MTU de mil cabeceras.
  • El colapso de throughput de TCP puede venir de tasas de pérdida pequeñas en redes con alta latencia; la sobrecarga de la VPN rara vez es el enemigo principal comparado con pérdida y RTT.

Tareas prácticas: comandos, salidas, decisiones

A continuación hay tareas prácticas que puedes ejecutar en hosts Linux (o dentro de VMs Linux) para encontrar el cuello de botella. Cada una incluye: el comando, lo que significa una salida típica,
y la decisión que deberías tomar después. Hazlas en orden si quieres velocidad sin superstición.

Task 1: Confirma que realmente estás probando sobre WireGuard

cr0x@server:~$ ip route get 10.60.0.10
10.60.0.10 dev wg0 src 10.60.0.1 uid 1000
    cache

Significado: El tráfico hacia 10.60.0.10 sale por wg0 con origen 10.60.0.1.
Decisión: Si no ves dev wg0, para. Arregla enrutamiento/AllowedIPs/policy routing primero o tus pruebas son basura.

Task 2: Inspecciona la salud del peer de WireGuard y si el endpoint cambia

cr0x@server:~$ sudo wg show wg0
interface: wg0
  public key: 2r4...redacted...Kk=
  listening port: 51820

peer: q0D...redacted...xw=
  endpoint: 203.0.113.44:51820
  allowed ips: 10.60.0.10/32, 10.20.0.0/16
  latest handshake: 28 seconds ago
  transfer: 18.42 GiB received, 25.11 GiB sent
  persistent keepalive: every 25 seconds

Significado: El handshake es reciente; el endpoint es estable; los contadores de tráfico se mueven.
Decisión: Si latest handshake es antiguo o el endpoint cambia inesperadamente, sospecha timeouts de NAT, roaming o problemas de estado de firewall—espera pérdida y jitter.

Task 3: Establece la línea base del underlay (throughput y latencia fuera de la VPN)

cr0x@server:~$ ping -c 5 203.0.113.44
PING 203.0.113.44 (203.0.113.44) 56(84) bytes of data.
64 bytes from 203.0.113.44: icmp_seq=1 ttl=53 time=19.8 ms
64 bytes from 203.0.113.44: icmp_seq=2 ttl=53 time=20.4 ms
64 bytes from 203.0.113.44: icmp_seq=3 ttl=53 time=19.9 ms
64 bytes from 203.0.113.44: icmp_seq=4 ttl=53 time=62.1 ms
64 bytes from 203.0.113.44: icmp_seq=5 ttl=53 time=20.2 ms

--- 203.0.113.44 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4005ms
rtt min/avg/max/mdev = 19.8/28.5/62.1/16.8 ms

Significado: El RTT del underlay tiene spikes (62 ms). La VPN amplificará eso en dolor de throughput de TCP.
Decisión: Si hay jitter/pérdida en el underlay, no esperes milagros de ajustes de MTU; puede que necesites gestión de colas o un camino/proveedor mejor.

Task 4: Mide el throughput del túnel con iperf3 (flujo único)

cr0x@server:~$ iperf3 -c 10.60.0.10 -t 15
Connecting to host 10.60.0.10, port 5201
[  5] local 10.60.0.1 port 43144 connected to 10.60.0.10 port 5201
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-1.00   sec  62.2 MBytes   522 Mbits/sec    0
[  5]   1.00-2.00   sec  61.8 MBytes   518 Mbits/sec    1
[  5]   2.00-3.00   sec  44.9 MBytes   377 Mbits/sec   12
[  5]   3.00-4.00   sec  58.2 MBytes   488 Mbits/sec    3
[  5]  14.00-15.00 sec  60.1 MBytes   504 Mbits/sec    2
- - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-15.00  sec   848 MBytes   474 Mbits/sec   44  sender
[  5]   0.00-15.00  sec   846 MBytes   473 Mbits/sec        receiver

Significado: El throughput es inestable y hay retransmisiones.
Decisión: Retransmisiones sobre una VPN suelen significar agujeros MTU, pérdida/jitter en el underlay, o problemas de buffering/colas. Siguiente: pruebas MTU y contadores de pérdida.

Task 5: Mide con flujos paralelos (para detectar límites por núcleo)

cr0x@server:~$ iperf3 -c 10.60.0.10 -P 8 -t 15
[SUM]   0.00-15.00  sec  3.62 GBytes  2.07 Gbits/sec  81  sender
[SUM]   0.00-15.00  sec  3.61 GBytes  2.07 Gbits/sec      receiver

Significado: La paralelización mejoró significativamente el throughput.
Decisión: Si -P 8 es mucho más rápido que flujo único, puede que estés limitado por CPU por flujo, o TCP esté sufriendo por pérdida/RTT. Revisa CPU y qdisc a continuación.

Task 6: Revisa MTU en wg0 y en la interfaz underlay

cr0x@server:~$ ip link show wg0
7: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/none
cr0x@server:~$ ip link show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff

Significado: wg0 por defecto en 1420, típico para underlay IPv4 con MTU 1500.
Decisión: Si el MTU del underlay es más pequeño de lo que crees (overlay de nube, PPPoE, etc.), 1420 aún puede ser demasiado alto. No adivines—prueba PMTU.

Task 7: Prueba PMTU con ping DF a través del túnel

cr0x@server:~$ ping -M do -s 1372 -c 3 10.60.0.10
PING 10.60.0.10 (10.60.0.10) 1372(1400) bytes of data.
1380 bytes from 10.60.0.10: icmp_seq=1 ttl=64 time=23.4 ms
1380 bytes from 10.60.0.10: icmp_seq=2 ttl=64 time=23.1 ms
1380 bytes from 10.60.0.10: icmp_seq=3 ttl=64 time=23.3 ms

--- 10.60.0.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms

Significado: Payload 1372 (1400 en la red para ICMP) funciona. Buena señal.
Decisión: Incrementa hasta que falle; el punto de fallo te dice el verdadero PMTU. Si falla inesperadamente bajo, probablemente tienes overhead de encapsulación o ICMP bloqueado.

cr0x@server:~$ ping -M do -s 1412 -c 3 10.60.0.10
PING 10.60.0.10 (10.60.0.10) 1412(1440) bytes of data.
ping: local error: message too long, mtu=1420
ping: local error: message too long, mtu=1420
ping: local error: message too long, mtu=1420

--- 10.60.0.10 ping statistics ---
3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2038ms

Significado: Tu wg0 MTU local te detiene en 1420—esto aún no es una prueba de ruta, es una limitación de interfaz.
Decisión: Si el PMTU real de la ruta es menor que el MTU de wg0, verás fallos en tamaños más pequeños también (o estancamientos extraños). Continúa probando cerca del MTU de wg0 y observa pérdida/retransmisiones.

Task 8: Comprueba si se reciben ICMP “frag needed” (PMTU funciona)

cr0x@server:~$ sudo ip -s -s link show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    RX:  bytes packets errors dropped  missed   mcast
    9012345678 8123456      0    1452       0  120345
    TX:  bytes packets errors dropped carrier collsns
    8123456789 7012345      0       0       0       0

Significado: Hay drops en RX (1452). Eso podría ser congestión, overflow de ring, o policing upstream.
Decisión: Si los drops suben durante iperf, trátalo como presión en la ruta de recepción del host o en el underlay. Investiga rings de NIC/interrupts/qdisc y shaping upstream.

Task 9: Observa la salud TCP (retransmisiones, congestión) durante una transferencia

cr0x@server:~$ ss -ti dst 10.60.0.10
ESTAB 0 0 10.60.0.1:43144 10.60.0.10:5201
	 cubic wscale:7,7 rto:204 rtt:24.1/2.1 ato:40 mss:1360 pmtu:1420 rcvmss:1360 advmss:1360 cwnd:64 bytes_acked:8123456 segs_out:6021 segs_in:5844 send 1.9Gbps lastsnd:8 lastrcv:8 lastack:8 pacing_rate 3.8Gbps delivery_rate 1.7Gbps retrans:12/44

Significado: MSS 1360, PMTU 1420. Hay retransmisiones.
Decisión: Retrans más un PMTU estable sugiere pérdida/jitter/cola, no solo desajuste MTU. Si MSS/PMTU parecen incorrectos (demasiado altos), arregla MTU/MSS clamp primero.

Task 10: Revisa saturación de CPU y softirq bajo carga

cr0x@server:~$ mpstat -P ALL 1 5
Linux 6.8.0 (server) 	12/27/2025 	_x86_64_	(8 CPU)

12:10:01 AM  CPU   %usr %nice  %sys %iowait  %irq %soft  %steal  %idle
12:10:02 AM  all   22.1  0.0   18.4   0.0    0.0  20.9    0.0   38.6
12:10:02 AM    0   12.0  0.0   10.1   0.0    0.0  62.3    0.0   15.6
12:10:02 AM    1   28.4  0.0   21.0   0.0    0.0   8.2    0.0   42.4
12:10:02 AM    2   30.1  0.0   25.7   0.0    0.0   5.9    0.0   38.3
12:10:02 AM    3   29.8  0.0   19.4   0.0    0.0   6.1    0.0   44.6
12:10:02 AM    4   18.0  0.0   15.2   0.0    0.0  28.0    0.0   38.8
12:10:02 AM    5   20.5  0.0   17.1   0.0    0.0  26.4    0.0   36.0
12:10:02 AM    6   19.7  0.0   15.9   0.0    0.0  25.1    0.0   39.3
12:10:02 AM    7   17.6  0.0   13.4   0.0    0.0  29.0    0.0   40.0

Significado: CPU0 tiene mucho en %soft (softirq). Eso suele ser procesamiento de recepción de red y puede limitar el throughput.
Decisión: Si el softirq domina en un CPU, probablemente necesites mejor distribución de IRQ (RSS/RPS), ajuste de colas de NIC, o mover la carga fuera de una VM pequeña.

Task 11: Revisa presión en buffers de socket WireGuard y UDP

cr0x@server:~$ netstat -su
Udp:
    2212345 packets received
    0 packets to unknown port received
    0 packet receive errors
    1832 packets sent
    0 receive buffer errors
    412 send buffer errors
UdpLite:
IpExt:
    InNoRoutes: 0
    InBcastPkts: 0

Significado: send buffer errors sugiere que el host no pudo encolocar UDP lo suficientemente rápido (o el qdisc/NIC está congestionado).
Decisión: Investiga qdisc, colas TX de NIC y potencialmente aumenta buffers de socket (con cuidado y midiendo). También revisa si hay policing de salida.

Task 12: Revisa qdisc y si estás usando algo sensato

cr0x@server:~$ tc qdisc show dev eth0
qdisc fq_codel 0: root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn

Significado: fq_codel es generalmente una buena elección por latencia y justicia.
Decisión: Si ves pfifo_fast o un gran backlog fq en otro lado, considera cambiar a fq_codel o cake (si está disponible) en el egress que importa.

Task 13: Verifica policy routing (común en setups de split tunnel)

cr0x@server:~$ ip rule show
0:	from all lookup local
1000:	from 10.60.0.0/24 lookup 100
32766:	from all lookup main
32767:	from all lookup default
cr0x@server:~$ ip route show table 100
default dev wg0 scope link

Significado: El tráfico con origen 10.60.0.0/24 usa la tabla 100 y por defecto va a wg0.
Decisión: Si las respuestas no usan la misma política, tendrás asimetría, estancamientos raros y “solo algunas apps van lentas.” Arregla reglas para que ambas direcciones coincidan.

Task 14: Busca problemas de conntrack/NAT que penalicen UDP

cr0x@server:~$ sudo conntrack -S
cpu=0 found=912345 invalid=12 ignore=0 insert=0 insert_failed=0 drop=0 early_drop=0 error=0 search_restart=120
cpu=1 found=901234 invalid=8 ignore=0 insert=0 insert_failed=0 drop=0 early_drop=0 error=0 search_restart=98

Significado: invalid existe pero no es exagerado; search_restart puede indicar presión en la tabla.
Decisión: Si invalid sube rápido bajo carga VPN, verifica reglas NAT, timeouts de UDP y si middleboxes reescriben o expiran estado demasiado agresivamente.

Task 15: Verifica offloads y si el driver de la NIC hace algo “creativo”

cr0x@server:~$ sudo ethtool -k eth0 | egrep 'gro|gso|tso|tx-checksum|rx-checksum'
rx-checksumming: on
tx-checksumming: on
tcp-segmentation-offload: on
generic-segmentation-offload: on
generic-receive-offload: on

Significado: Los offloads están activados. Normalmente es bueno. Ocasionalmente es desastroso con ciertas NICs virtuales o drivers buggeados.
Decisión: Si ves CPU alta, comportamiento extraño en capturas o colapsos de throughput tras una actualización de kernel/driver, prueba deshabilitar GRO en wg0 o en el underlay como experimento controlado—luego revierte si no ayuda.

Task 16: Captura evidencia sin engañarte (tcpdump con claridad)

cr0x@server:~$ sudo tcpdump -ni eth0 udp port 51820 -c 5
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
01:12:01.123456 IP 198.51.100.10.51820 > 203.0.113.44.51820: UDP, length 148
01:12:01.143211 IP 203.0.113.44.51820 > 198.51.100.10.51820: UDP, length 92
01:12:01.163001 IP 198.51.100.10.51820 > 203.0.113.44.51820: UDP, length 1200
01:12:01.182992 IP 198.51.100.10.51820 > 203.0.113.44.51820: UDP, length 1200
01:12:01.203114 IP 203.0.113.44.51820 > 198.51.100.10.51820: UDP, length 92
5 packets captured

Significado: Ves tráfico UDP bidireccional en el puerto de WireGuard. Bien.
Decisión: Si el tráfico es solo en una dirección, los problemas de rendimiento son secundarios—tienes una falla de ruta/firewall/NAT en una dirección.

MTU y fragmentación: la mentira más común de “está bien”

Cuando WireGuard está lento, MTU es culpable con suficiente frecuencia como para tratarlo como sospechoso por defecto—pero no como reparación por defecto. El enfoque correcto es:
determinar el path MTU efectivo, luego ajustar el MTU de wg0 (o clamping de MSS) para que tu tráfico nunca dependa de que los paquetes fragmentados se entreguen de forma fiable.

Qué sucede realmente

WireGuard encapsula tus paquetes dentro de UDP. Eso significa cabeceras extra. Si tu paquete interno está dimensionado para una ruta de 1500 bytes, el paquete externo puede
superar 1500 y o bien:

  • ser fragmentado (mejor caso: los fragmentos llegan; peor caso: los fragmentos se pierden), o
  • ser descartado con un ICMP “fragmentation needed” (si PMTUD funciona), o
  • ser descartado silenciosamente (agujero PMTUD, el clásico).

Los patrones de fallo son distintivos:

  • Paquetes pequeños OK, paquetes grandes fallan: SSH funciona, copia de archivos se queda, páginas web cargan parcialmente.
  • Dientes en el throughput: TCP sube, golpea un muro, colapsa, y se repite.
  • “Se arregla” en redes diferentes: porque la PMTU varía entre rutas y políticas de filtrado ICMP.

No idolatres 1420

1420 es un valor razonable por defecto para un underlay IPv4 con MTU 1500. Pero:

  • Si ejecutas WireGuard sobre underlay IPv6, el overhead difiere.
  • Si tu enlace “1500 MTU” es en realidad un overlay (SDN de nube) o PPPoE, puede que tengas menos.
  • Si hay IPSec, GRE, VXLAN o middleboxes “serviciales”, tienes menos.

Dos estrategias sólidas

  1. Fija el MTU de wg0 a un valor conocido y seguro.
    Esto afecta todo el tráfico, incluidos apps basados en UDP. Es contundente pero fiable.
  2. Clampa el MSS de TCP en el ingreso/egreso del túnel.
    Esto solo afecta TCP y puede preservar MTU mayor para flujos UDP que manejan la fragmentación (o no envían cargas grandes).

Flujo de trabajo práctico de MTU que puedas defender

  1. Ejecuta pings DF al IP lejano del túnel (Task 7), encuentra el mayor payload que pasa de forma fiable.
  2. Resta el overhead correcto si estás probando diferentes capas (sé consistente).
  3. Fija el MTU de wg0 a un valor que deje margen, no uno que “apenas pase en un buen día”.
  4. Vuelve a ejecutar iperf3 y compara retransmisiones y estabilidad, no solo throughput pico.

Ejemplo: fijar MTU de forma segura

cr0x@server:~$ sudo ip link set dev wg0 mtu 1380
cr0x@server:~$ ip link show wg0
7: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1380 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/none

Significado: El MTU de wg0 ahora es 1380.
Decisión: Si el rendimiento se estabiliza (menos retransmisiones, throughput más consistente), déjalo y documenta la PMTU medida. Si nada cambia, MTU no era el cuello de botella principal.

Ejemplo: clamping de MSS (iptables)

cr0x@server:~$ sudo iptables -t mangle -A FORWARD -o wg0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
cr0x@server:~$ sudo iptables -t mangle -S FORWARD | tail -n 2
-A FORWARD -o wg0 -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j TCPMSS --clamp-mss-to-pmtu

Significado: Los paquetes SYN TCP saliendo por wg0 tendrán el MSS clamped.
Decisión: Usa esto cuando no puedas controlar MTU end-to-end (multi-peer, clientes en roaming), o cuando solo veas problemas en TCP. Valida con ss -ti que MSS/PMTU coincidan.

Enrutamiento y policy routing: cuando los paquetes toman la ruta escénica

Los errores de enrutamiento no siempre rompen la conectividad. La degradan de formas que parecen “VPN lenta” mientras la causa raíz es “los paquetes están de turismo raro.” Se ve más en setups de split‑tunnel, servidores multihomed y entornos con rutas por defecto que cambian (hola, laptops).

AllowedIPs es enrutamiento, no control de acceso

El campo AllowedIPs de WireGuard es multipropósito: le dice al kernel qué destinos deben enrutarse a ese peer, y también actúa como una tabla de enrutamiento por clave criptográfica. Lo importante: si lo defines mal, el tráfico puede enviarse al peer equivocado, o no enviarse al túnel, o enviarse con la dirección origen equivocada.

Enrutamiento asimétrico: el asesino silencioso del throughput

Tu tráfico saliente puede ir por WireGuard, pero las respuestas pueden volver por el underlay, o por otro túnel, o por un camino NAT que rompe el estado. TCP odia la asimetría. UDP también la sufre, solo que menos ruidoso.

Cómo atrapar mentiras de enrutamiento rápido

  • Usa ip route get para el destino desde el emisor (Task 1).
  • Usa ip rule y ip route show table X cuando haya policy routing (Task 13).
  • Revisa reverse path filtering (rp_filter) si tienes múltiples interfaces y policy routing.

Reverse path filtering: “feature de seguridad” que se vuelve bug de disponibilidad

cr0x@server:~$ sysctl net.ipv4.conf.all.rp_filter net.ipv4.conf.eth0.rp_filter net.ipv4.conf.wg0.rp_filter
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 1
net.ipv4.conf.wg0.rp_filter = 0

Significado: rp_filter estricto está habilitado globalmente y en eth0. En setups con policy routing, eso puede descartar respuestas asimétricas legítimas.
Decisión: Si ves drops inexplicables y asimetría, pon rp_filter a 2 (loose) en las interfaces implicadas o desactívalo donde corresponda—luego verifica con contadores de paquetes.

cr0x@server:~$ sudo sysctl -w net.ipv4.conf.all.rp_filter=2
net.ipv4.conf.all.rp_filter = 2

Significado: Modo loose. Sigue dando algunas verificaciones de sanity sin romper policy routing.
Decisión: Hazlo persistente solo después de confirmar que resuelve el problema y no viola tu modelo de amenaza.

CPU y criptografía: cuando el conteo de núcleos es un límite de throughput

WireGuard es rápido. Pero “rápido” no es “gratis”, y el underlay puede ser lo bastante veloz como para exponer la CPU como el techo. En instancias pequeñas en la nube, Xeons antiguos, hypervisors ocupados o hosts haciendo mucho trabajo de conntrack/firewall, puedes perfectamente fijar un núcleo y dejar de escalar.

Cómo se ve un cuello de botella de CPU

  • iperf3 se estabiliza en un bitrate estable, sin importar la capacidad del enlace.
  • Un núcleo está pegado en %soft o %sys mientras otros están mayormente inactivos.
  • Streams paralelos de iperf3 aumentan el throughput más de lo “esperado”.
  • El tiempo de sistema crece con la tasa de paquetes, no con los bytes transferidos.

Distinguir coste de criptografía del coste de procesamiento de paquetes

A la gente le encanta culpar a la criptografía porque suena sofisticado. A menudo, el problema real es el overhead de procesamiento de paquetes: interrupts, softirq, comportamiento GRO/GSO,
conntrack y qdisc. La crypto de WireGuard es eficiente; el camino de red de tu host puede no serlo.

Revisa tiempo de kernel y softirq

cr0x@server:~$ top -b -n 1 | head -n 15
top - 01:20:11 up 16 days,  3:12,  1 user,  load average: 3.21, 2.88, 2.44
Tasks: 213 total,   2 running, 211 sleeping,   0 stopped,   0 zombie
%Cpu(s): 18.2 us,  0.0 ni, 27.6 sy,  0.0 id,  0.0 wa,  0.0 hi, 54.2 si,  0.0 st
MiB Mem :  16000.0 total,   2200.0 free,   4100.0 used,   9700.0 buff/cache
MiB Swap:   2048.0 total,   2048.0 free,      0.0 used.  11200.0 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
  934 root      20   0       0      0      0 S  62.0   0.0  31:22.11 ksoftirqd/0
 2112 root      20   0       0      0      0 S  18.0   0.0  12:04.22 ksoftirqd/4

Significado: Los hilos de softirq están consumiendo CPU. Eso es overhead de procesamiento de red.
Decisión: Investiga afinidad de IRQ, número de colas NIC, RPS/XPS, y si tu VM está capada o pegada a un vecino ruidoso.

Distribución de interrupts y colas

cr0x@server:~$ grep -E 'eth0|wg0' /proc/interrupts | head
  24:  8123456        0        0        0        0        0        0        0   PCI-MSI 327680-edge      virtio0-input.0
  25:        0  7012345        0        0        0        0        0        0   PCI-MSI 327681-edge      virtio0-output.0

Significado: Los interrupts están concentrados en CPUs específicas.
Decisión: Si la mayoría de interrupts llegan a un CPU, configura afinidad de IRQ y asegúrate de que multi-queue esté habilitado. El objetivo: repartir el procesamiento de paquetes entre núcleos sin crear caos de caché.

Reality check de aceleración criptográfica

ChaCha20 de WireGuard rinde bien en muchas CPU, incluso sin AES‑NI. Pero la historia cambia con:

  • Ratios de paquetes muy altos (paquetes pequeños): el overhead lo dominan los costes por paquete.
  • VMs con pocas vCPU y mal tuning de virtio.
  • Firewalls haciendo mucho conntrack en el mismo host.

Si tu techo es la CPU: escala la instancia, externaliza el firewalling, o termina el túnel en una máquina diseñada para empujar paquetes. A veces la “solución” es comprar una VM más grande. No es vergonzoso; sale más barato que tiempo de equipo.

Offloads de NIC, rarezas de checksum y el impuesto de la VM

WireGuard vive en el kernel, lo cual es bueno. Pero aún depende de drivers de NIC y del stack de red de Linux. Los offloads (GRO/GSO/TSO) normalmente
mejoran throughput y reducen CPU. A veces interactúan mal con túneles, NICs virtuales o filtros de paquetes.

Síntomas que huelen a problemas de offload

  • Las capturas muestran paquetes “gigantes” que no existen en la red física.
  • El rendimiento cambia drásticamente después de un update de kernel, sin cambios de configuración.
  • El throughput es bueno en una dirección pero terrible en la otra.
  • CPU alta en softirq más drops inexplicables en el host.

Experimento controlado: deshabilitar GRO en wg0

cr0x@server:~$ sudo ethtool -K wg0 gro off
Cannot get device settings: Operation not supported

Significado: La interfaz WireGuard no soporta toggles estándar de ethtool igual que una NIC física.
Decisión: Usa sysctls y céntrate en offloads del underlay NIC. Para WireGuard en sí, enfócate en MTU, enrutamiento y comportamiento CPU/IRQ del host.

Más relevante: toggles de offload en el underlay (probar, no comprometer a ciegas)

cr0x@server:~$ sudo ethtool -K eth0 gro off gso off tso off
cr0x@server:~$ sudo ethtool -k eth0 | egrep 'gro|gso|tso'
tcp-segmentation-offload: off
generic-segmentation-offload: off
generic-receive-offload: off

Significado: Los offloads están deshabilitados. El uso de CPU subirá; la tasa de paquetes procesados aumentará.
Decisión: Si deshabilitar offloads arregla la estabilidad del throughput del túnel (raro, pero real), probablemente tienes un problema de driver/virtualización. Mantén el workaround a corto plazo; planifica un fix de kernel/driver/hypervisor.

El impuesto de la VM

En muchos hypervisores, un endpoint VPN dentro de una VM paga overhead extra:

  • Los drivers virtio/netfront y el procesamiento en vSwitch añaden latencia y coste de CPU.
  • La coalescencia de interrupts y la planificación de vCPU pueden crear entrega en ráfagas.
  • Los proveedores cloud pueden rate‑limitar UDP o depriorizarlo bajo congestión.

Si intentas empujar multi‑gigabit por una VM pequeña, no se convertirá en grande por optimismo positivo.

Realidades de UDP: pérdida, jitter, buffers y shaping

WireGuard usa UDP. Eso es una ventaja, pero significa que heredas el comportamiento del underlay sin una capa de retransmisión integrada. Si el underlay
descarta o reordena, el túnel no lo “arregla”. TCP dentro del túnel lo notará y te castigará con ventanas de congestión reducidas y retransmisiones.

Qué medir cuando “a veces va lento”

  • Pérdida: drops en interfaz, errores UDP, retransmisiones TCP.
  • Jitter: varianza en ping y tiempos a nivel de aplicación.
  • Colas: estadísticas de qdisc, síntomas de bufferbloat (spikes de latencia bajo carga).
  • Policing: shaping de egress en la nube o rate limits de middleboxes (a menudo duros para ráfagas UDP).

Revisa estadísticas de qdisc para drops/overlimits

cr0x@server:~$ tc -s qdisc show dev eth0
qdisc fq_codel 0: root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn
 Sent 912345678 bytes 701234 packets (dropped 123, overlimits 0 requeues 12)
 backlog 0b 0p requeues 12

Significado: Ocurrieron drops en qdisc. Esa es congestión local o shaping.
Decisión: Si los drops se correlacionan con los ralentíes, atiende el encolamiento de salida: asegura fq_codel/cake, reduce la carga ofrecida, o arregla el shaping upstream en lugar de culpar a WireGuard.

Buffers de socket: solo después de ver errores de buffer

A la gente le encanta subir rmem_max y wmem_max. A veces ayuda; a menudo solo aumenta latencia y uso de memoria mientras el problema real es un uplink congestionado.

cr0x@server:~$ sysctl net.core.rmem_max net.core.wmem_max
net.core.rmem_max = 212992
net.core.wmem_max = 212992

Significado: Buffers máximos por defecto.
Decisión: Solo aumenta si tienes evidencia de errores de buffer (netstat -su) y entiendes hacia dónde se moverá el encolamiento. Mide después de cada cambio.

cr0x@server:~$ sudo sysctl -w net.core.rmem_max=8388608
net.core.rmem_max = 8388608
cr0x@server:~$ sudo sysctl -w net.core.wmem_max=8388608
net.core.wmem_max = 8388608

Significado: Permitidos buffers máximos más grandes.
Decisión: Vuelve a ejecutar iperf3 y revisa netstat -su. Si los errores bajan y el throughput se estabiliza sin que la latencia explote, mantenlo; si no, revierte y mira congestión del underlay.

Broma #2: UDP es como el chisme de oficina—rápido, ligero, y si algo se pierde, nadie vuelve a corregir el registro.

Tres mini-historias corporativas desde el campo

Incidente causado por una suposición equivocada: “MTU siempre es 1500 en la nube”

Una empresa mediana tenía una flota de gateways WireGuard en un proveedor cloud importante. En staging todo parecía bien. En producción, cada volcado nocturno
de base de datos sobre el túnel “a veces” se estancaba. No fallaba—se estancaba. Los operadores veían la transferencia congelarse por minutos y luego reanudarse.

La reacción inicial fue predecible: culpar a la base de datos, luego a la herramienta de backup, luego al almacenamiento. Alguien sugirió “quizá WireGuard está lento.”
Bajaron el MTU a 1280 porque lo vieron en un blog. Mejoró un poco, luego regresó la semana siguiente tras un cambio en la ruta de la red cloud.

La suposición equivocada fue pensar que el underlay era un simple Ethernet de 1500 bytes. No lo era. La red del proveedor añadía encapsulación,
y la PMTU efectiva difería entre zonas de disponibilidad. PMTUD debería haberlo manejado, pero los mensajes ICMP “fragmentation needed” eran filtrados por un
appliance de seguridad que se había instalado “temporalmente” y luego se volvió permanente—como muchas cosas corporativas.

La solución fue aburrida: permitir los tipos ICMP relevantes, fijar el MTU de wg0 basado en PMTU medida para la peor ruta, y clamar MSS por seguridad. Las copias nocturnas
se estabilizaron y el equipo dejó de alertar a los ingenieros de almacenamiento por un problema de red disfrazado de almacenamiento.

La lección: si no has medido PMTU end-to-end, no tienes un MTU. Tienes un sistema de creencias.

Una optimización que salió mal: “Deshabilitar qdisc para reducir overhead”

Un equipo de plataforma interno quiso throughput máximo entre dos centros de datos. Leyeron que los qdiscs pueden añadir overhead y decidieron
“simplificar” cambiando el egress a un FIFO básico y subir los buffers. Los gráficos lo adoraron al principio: mayor throughput pico en pruebas de laboratorio.

Luego llegó producción. Durante el tráfico diurno, una transferencia grande provocaba spikes de latencia en servicios no relacionados. El túnel era compartido por
varias apps, y la cola FIFO se convirtió en una máquina de latencia. Los timeouts RPC subieron. Los retries aumentaron la carga. La tormenta auto-infligida clásica.

Los ingenieros intentaron echar la culpa a WireGuard. Pero la pérdida de paquetes no era el problema principal; era el encolamiento y la latencia. La VPN era simplemente
donde convergía el tráfico, así que la culparon como la única persona en la sala con camisa brillante.

Volver a fq_codel estabilizó la latencia y mejoró el throughput efectivo para flujos interactivos. El throughput pico de la transferencia grande cayó un poco. A nadie le importó, porque al negocio le importaba “los sistemas siguen arriba”, no “un número de benchmark se ve bonito.”

La lección: si optimizas throughput sin controlar el encolamiento, estás construyendo un mecanismo de denegación de servicio y llamándolo rendimiento.

Una práctica aburrida pero correcta que salvó el día: “Medir, registrar y volver a probar tras cada cambio”

Una compañía con control de cambios estricto ejecutaba WireGuard entre on‑prem y la nube. Tenían una queja recurrente: “la VPN está lenta.” En vez de lanzarse a tunear, un SRE escribió un pequeño runbook: una prueba iperf3, una prueba MTU, una verificación de enrutamiento, una comprobación de CPU. Los resultados iban a un ticket.

En unas semanas aparecieron patrones. Los ralentíes coincidían con un peering ISP específico y aumento de jitter en el underlay. Otro subconjunto de incidentes correlacionó con un tamaño de VM particular usado para el gateway: se veía steal de CPU alto en horas punta.

Cuando golpeó un slowdown mayor durante un release, el equipo no discutió. Ejecutaron las mismas pruebas. El ping del underlay mostró jitter. iperf3 mostró retransmisiones. CPU estaba bien. El enrutamiento correcto. Eso eliminó la mitad de la especulación usual en cinco minutos.

Rerutearon temporalmente tráfico por otro gateway en mejor ruta y abrieron una escalada con el ISP con evidencia limpia. La VPN no se “arregló” con un sysctl mágico. Se arregló sabiendo dónde estaba el problema.

La lección: la disciplina de medición aburrida es un multiplicador de fuerza. No parece heroica, pero evita conjeturas costosas.

Errores comunes: síntoma → causa raíz → solución

1) Síntoma: SSH funciona, descargas grandes se estancan o cuelgan

Causa raíz: Agujero PMTU o desajuste de MTU; drops por fragmentación; ICMP bloqueado.

Solución: Mide PMTU con pings DF; baja el MTU de wg0; clampa MSS de TCP; permite ICMP “fragmentation needed” en la ruta.

2) Síntoma: Throughput está bien con iperf3 -P 8 pero mal con flujo único

Causa raíz: TCP de flujo único limitado por RTT/pérdida o CPU por flujo; la ventana de congestión no puede crecer.

Solución: Revisa retransmisiones y jitter; arregla MTU/pérdida primero; si es CPU, mejora distribución de IRQ o escala el gateway.

3) Síntoma: El túnel va lento solo desde un sitio / un ASN / una Wi‑Fi

Causa raíz: Ruta underlay con pérdida/jitter o shaping del proveedor; a veces UDP tratado mal.

Solución: Establece línea base de ping/jitter del underlay; compara desde múltiples redes; considera cambiar puerto del endpoint, pero solo después de verificar MTU y enrutamiento.

4) Síntoma: Rápido por minutos, luego colapsa, luego se recupera

Causa raíz: Encolamiento/bufferbloat, o drops transitorios; a veces flapping de estado NAT sin keepalive.

Solución: Usa fq_codel/cake en egress; monitoriza drops de qdisc; configura PersistentKeepalive para peers en roaming/NAT; revisa conntrack.

5) Síntoma: Una dirección es mucho más lenta que la otra

Causa raíz: Enrutamiento asimétrico, policing de salida, o distinto MTU/PMTU en cada camino.

Solución: Ejecuta comprobaciones de enrutamiento en ambos extremos; verifica policy routing y rp_filter; compara estadísticas de qdisc y drops por dirección en las interfaces.

6) Síntoma: El rendimiento empeoró tras un update de kernel/driver/hypervisor

Causa raíz: Cambió comportamiento de offloads; regresión en virtio o driver de NIC; moderación de interrupts distinta.

Solución: Prueba toggles de offload como workaround temporal; ajusta afinidad de IRQ; planifica upgrade/rollback con evidencia.

7) Síntoma: “WireGuard está lento” solo en un gateway que también hace firewalling

Causa raíz: conntrack y reglas iptables/nft añaden coste CPU; presión de softirq; contención en tablas.

Solución: Reduce conntrack para tráfico de túnel donde sea seguro, simplifica reglas, escala CPU, o separa roles (endpoint VPN vs firewall stateful).

8) Síntoma: Spikes de latencia bajo carga, incluso si el throughput es aceptable

Causa raíz: Mal qdisc (FIFO), buffers demasiado grandes, o shaping sin AQM.

Solución: Usa fq_codel/cake; evita “simplemente aumentar buffers”; mide ping bajo carga.

Listas de verificación / plan paso a paso (seguro, aburrido, efectivo)

Checklist A: Diagnóstico de una hora en un incidente de producción

  1. Confirma enrutamiento: ip route get al IP remoto del túnel. Si no es wg0, arregla routing/AllowedIPs primero.
  2. Confirma salud del túnel: wg show para verificar handshake reciente y contadores en movimiento.
  3. Ejecuta iperf3: flujo único y -P 8. Anota retransmisiones y estabilidad.
  4. Prueba MTU: pings DF al peer del túnel; busca agujeros.
  5. Chequeo CPU/softirq: mpstat / top. Si un núcleo está pegado, trátalo como limitación del host.
  6. Pérdida y encolamiento: ip -s link drops; tc -s qdisc drops/overlimits.
  7. Sanidad NAT/conntrack: estadísticas conntrack, contadores de firewall si están disponibles.
  8. Decide: arreglar MTU/MSS, arreglar enrutamiento, escalar CPU del gateway, o escalar al proveedor del underlay con evidencia.

Checklist B: Endurecer un despliegue WireGuard para rendimiento predecible

  1. Elige una estrategia de MTU objetivo: fija MTU de wg0 basada en la PMTU medida peor-caso, y/o clampa MSS para TCP.
  2. Estandariza qdisc: fq_codel en egress WAN; evita FIFO a menos que disfrutes incidentes de latencia.
  3. Planifica capacidad de CPU: benchmark con iperf3 y mide softirq por núcleo antes de declarar “está bien”.
  4. Documenta intención de enrutamiento: AllowedIPs, reglas policy, enrutamiento basado en origen; incluye la razón en comentarios de config.
  5. Decide política de keepalive: clientes en roaming y rutas NAT-heavy necesitan PersistentKeepalive; servidores en enlaces estables suelen no necesitarlo.
  6. Instrumenta: exporta drops de interfaz, drops de qdisc, softirq CPU y edad de handshake; alerta en tendencias, no en spikes aislados.

Checklist C: Plan de cambio para un problema sospechado de MTU (con rollback)

  1. Mide línea base con iperf3 y ss -ti retransmisiones.
  2. Mide con pings DF el payload máximo que pasa de forma fiable.
  3. Reduce el MTU de wg0 por una pequeña cantidad justificada (p. ej., 20–40 bytes) o implementa MSS clamp.
  4. Vuelve a probar con iperf3 y observa el cambio en retransmisiones.
  5. Revierte si no hay mejora; MTU no era el cuello de botella.
  6. Escribe la PMTU medida y el valor elegido para que nadie lo “optimice” después.

Preguntas frecuentes

1) ¿Qué throughput debería esperar de WireGuard?

En hardware moderno, WireGuard puede alcanzar multi‑gigabit. En la práctica, tu techo suele ser CPU, comportamiento de NIC/driver y pérdida/jitter del underlay.
Mide con iperf3 y observa softirq por núcleo.

2) ¿1420 es siempre el MTU correcto para wg0?

No. Es un valor razonable por defecto para un underlay IPv4 de 1500 bytes. Overlays en la nube, PPPoE y túneles anidados pueden requerir MTUs menores. Mide PMTU y ajusta el MTU con evidencia.

3) ¿Debería usar MSS clamping o fijar MTU?

Si el problema es mayormente TCP y tienes clientes/rutas diversas, clamping MSS suele ser la solución menos disruptiva. Si transportas mucho tráfico UDP (VoIP, juegos, cargas QUIC) y la PMTU es poco fiable, fijar MTU de wg0 a un valor seguro es más claro.

4) ¿Por qué iperf3 -P 8 se ve genial pero el flujo único es mediocre?

Los streams paralelos ocultan limitaciones de flujo único (RTT, sensibilidad a pérdida, comportamiento del control de congestión) y pueden distribuir costes de CPU. Si el tráfico real es de flujo único (backups, descargas de objetos), optimiza para eso: arregla pérdida/MTU y reduce encolamiento.

5) ¿Las reglas de firewall pueden hacer que WireGuard vaya lento?

Absolutamente. El filtrado stateful y conntrack pueden añadir coste CPU y descartar paquetes bajo carga. Además, los firewalls que bloquean ICMP “fragmentation needed” pueden romper PMTUD y causar el clásico síntoma “paquetes pequeños bien, paquetes grandes mueren”.

6) ¿WireGuard es más lento que OpenVPN?

A menudo es más rápido, especialmente en modo kernel. Pero si tu cuello de botella es un agujero MTU, asimetría de enrutamiento o pérdida del underlay, cambiar de VPN no solucionará la física ni la política. Diagnostica primero.

7) ¿Cambiar el puerto de WireGuard ayuda al rendimiento?

A veces, pero no es una solución de primera línea. Cambiar puerto puede evadir rate limits toscos o middleboxes rotos. Haz las comprobaciones de MTU/enrutamiento/CPU antes para no “arreglar” el problema equivocado por accidente.

8) ¿Por qué WireGuard va lento solo en una red de laptop?

Probablemente PMTU/filtrado ICMP en esa red, o shaping/pérdida UDP en esa ruta. Los clientes en roaming detrás de NAT también pueden sufrir si no se pone keepalive. Compara pings DF y observa la estabilidad del handshake.

9) ¿Debería aumentar buffers de socket de Linux para WireGuard?

Solo si ves evidencia como errores en buffers UDP. Buffers más grandes pueden aumentar latencia y ocultar congestión. Arregla colas y pérdida primero; afina buffers después.

Próximos pasos que puedes ejecutar esta semana

  1. Elige una prueba reproducible (iperf3 flujo único + -P 8) y registra baseline de throughput, retransmisiones y CPU.
  2. Ejecuta pruebas DF ping PMTU a través del túnel y decide MTU o MSS clamp basándote en el mayor tamaño fiable, no en sensaciones.
  3. Valida enrutamiento de forma determinista con ip route get y ip rule en ambos extremos; caza asimetría.
  4. Observa softirq durante la carga; si un núcleo está pegado, arregla distribución de IRQ o escala el endpoint VPN.
  5. Revisa drops de qdisc y pasa a fq_codel donde corresponda; deja de usar FIFO si la latencia importa (y siempre importa).
  6. Escribe un runbook de una página con los comandos exactos que ejecutaste hoy y el “si la salida se ve X, haz Y”.

WireGuard no es “lento”. Tu ruta, tu MTU, tu enrutamiento o tu CPU son lentos. La buena noticia es que esos problemas son diagnosables con unos pocos comandos y la negativa a adivinar.

← Anterior
Mejor configuración Proxmox para homelab en 2026: hardware, NICs, diseño de almacenamiento y eficiencia energética
Siguiente →
Errores upstream de Nginx en Docker: depura 502/504 con los logs correctos

Deja un comentario