Fix WireGuard MTU Black Holes on a VPS
Last edited on August 2, 2026

A WireGuard tunnel can show a recent handshake, pass a small ping, and still fail when it carries a web page, file transfer, RDP session, database response, or routed subnet. That pattern usually means reachability is working but packet size is not. An encrypted packet grows when WireGuard adds its header, UDP, and an outer IP header. If one hop cannot carry that outer packet and the sender never receives useful Path MTU feedback, larger traffic disappears into a PMTU black hole.

Do not begin by copying MTU = 1420 from another server. wg-quick already derives an interface MTU from endpoint routing or the default route when no override is present, according to the official wg-quick(8) documentation. That automatic choice is often sensible. A repair becomes defensible only after you identify which path, address family, direction, and traffic class fails.

This guide builds that evidence on a Linux VPS, applies one reversible change, and closes the incident with real workload tests. Keep provider console access or another independent administration path open throughout the work. A remote tunnel should never be its own only recovery method.

A live tunnel can still drop real traffic

WireGuard’s handshake proves that two peers can exchange small encrypted UDP packets and authenticate each other. It does not prove that a full-sized inner packet can be encapsulated and cross every underlay hop. Transfer counters prove bytes moved, but they do not reveal whether the useful workload stopped at a particular packet size.

IPv4 Path MTU Discovery relies on an ICMP “fragmentation needed” response when a router cannot forward a packet whose Don’t Fragment bit is set. RFC 1191 defines that behavior. IPv6 routers do not fragment forwarded packets; RFC 8201 uses ICMPv6 Packet Too Big feedback instead. The RFC names a recognizable failure: a TCP three-way handshake succeeds, then data transfer hangs because Packet Too Big messages never reach the sender.

That distinction matters during triage. A stale handshake points toward endpoint reachability, keys, UDP filtering, NAT state, or AllowedIPs. A current handshake combined with size-sensitive failure moves MTU and PMTUD much higher on the list, but it is still a hypothesis to prove, not permission to change every interface.

Record the symptom before touching the tunnel

Capture a UTC timestamp, current handshake age, transfer counters, interface MTU, interface errors, and both route decisions. Use addresses rather than hostnames for the first pass so DNS cannot hide the real problem.

date -u
sudo wg show wg0 latest-handshakes
sudo wg show wg0 transfer
ip -details -statistics link show dev wg0
ip route get <WIREGUARD_ENDPOINT_IP>
ip route get <TUNNEL_DESTINATION_IP>

Replace the placeholders with one actual peer endpoint and one address reached inside the tunnel. Save the output in the incident record, but redact public keys, endpoint addresses, and private topology before sharing it outside the team. WireGuard’s official Quick Start confirms that wg show is the intended state view while ordinary Linux routing and interface tools expose the surrounding network.

Also write down one failing workload and one small operation that succeeds. “VPN is slow” is too vague. “A 56-byte ping succeeds, an HTTPS response stalls after headers, and one direction of a 1 MiB transfer never completes” gives the experiment something concrete to reproduce.

Map both packet sizes before changing MTU

Two paths are involved. The underlay carries encrypted UDP between public endpoints. The inner path carries application packets through wg0. Testing only one can produce a plausible number that is still wrong.

Confirm which interface carries each path

ip route get should show the public endpoint leaving through the VPS network interface and the private destination leaving through wg0. If the public endpoint itself routes into wg0, fix that recursion before investigating MTU. If policy routing or multiple tables are present, add the real source address to the query:

ip route get <WIREGUARD_ENDPOINT_IP> from <VPS_PUBLIC_IP>
ip route get <TUNNEL_DESTINATION_IP> from <VPS_TUNNEL_IP>
ip rule show
ip route show table all

Complex routing output can contain internal addresses. Store it with the incident rather than pasting it into a public ticket.

Measure each boundary separately: endpoint route, encrypted outer path, and inner traffic path.

inner application packet
        |
        |  test through wg0: ping -M do, TCP/UDP workload
        v
[ wg0 interface MTU ]
        |
        |  WireGuard data header + UDP + outer IP
        v
[ encrypted outer packet ] ---- public endpoint route ----> [ peer ]
        ^                         ^
        |                         |
   ip link show             tracepath / DF probe

This evidence map prevents a common mistake: lowering the physical interface because traffic inside the tunnel stalls. The VPS NIC may correctly remain at 1500 while the WireGuard interface needs a smaller ceiling for a constrained Internet path.

Account for outer IPv4 and IPv6 separately

A typical WireGuard data packet adds 60 bytes over outer IPv4: 20 bytes of IPv4, 8 bytes of UDP, and 32 bytes of WireGuard data-message overhead. Outer IPv6 uses 40 bytes for its IP header, so the common total becomes 80 bytes. WireGuard’s official protocol description documents UDP transport and the authenticated data-message structure. IP options, packet padding, another tunnel, or an unusual path can reduce the usable ceiling further; measurement remains authoritative.

If a peer endpoint can resolve or roam between IPv4 and IPv6, use the lowest path that the deployment must support. One WireGuard interface has one MTU even when its peers travel across different underlays. A highly constrained peer can therefore force a conservative value for everyone on that interface. When the compromise becomes material, separate peers by interface instead of pretending one number is optimal for every path.

Voxfor’s VPS hosting platform gives operators kernel-network and firewall control, but that control also makes packet-path ownership explicit. Keep console access available before changing remote routing or MTU.

Prove where packets disappear

Run probes from both ends when possible. Paths can be asymmetric, and a successful client-to-VPS test does not prove the return direction. Keep probe counts low on production systems.

Bracket the underlay PMTU

Start with tracepath toward the public endpoint. It can report an estimated PMTU without requiring root, although filtered or asymmetric paths may hide the useful response.

tracepath -n <WIREGUARD_ENDPOINT_IPV4>
tracepath -6 -n <WIREGUARD_ENDPOINT_IPV6>

Then use controlled ping probes. Linux ping -M do requests Path MTU behavior with Don’t Fragment semantics. The ping(8) manual also explains that -s is payload size: add 28 bytes for IPv4 ICMP or 48 bytes for IPv6 ICMP to compare the result with an IP MTU.

# Examples for a suspected 1500-byte underlay; adjust and bracket downward.
ping -4 -M do -s 1472 -c 3 <WIREGUARD_ENDPOINT_IPV4>
ping -4 -M do -s 1400 -c 3 <WIREGUARD_ENDPOINT_IPV4>

ping -6 -M do -s 1452 -c 3 <WIREGUARD_ENDPOINT_IPV6>
ping -6 -M do -s 1380 -c 3 <WIREGUARD_ENDPOINT_IPV6>

Do not treat one timeout as a size verdict because some hosts ignore Echo Request. The useful pattern is a smaller probe that consistently succeeds while a larger one consistently fails, ideally accompanied by fragmentation-needed or Packet Too Big feedback. Record the largest repeatable success in each direction.

Probe the inner ceiling through wg0

Inspect the current tunnel MTU, then test a peer tunnel address. For IPv4, an ICMP payload equal to wg0 MTU - 28 should fit. For IPv6, use wg0 MTU - 48. Test one byte above the calculated payload only in a controlled window; a local “message too long” is useful evidence that the host enforced the interface boundary.

ip -o link show dev wg0

# Example only: when wg0 reports MTU 1420.
ping -4 -I wg0 -M do -s 1392 -c 3 <PEER_TUNNEL_IPV4>
ping -6 -I wg0 -M do -s 1372 -c 3 <PEER_TUNNEL_IPV6>

If the peer does not answer ICMP, use a service you own and can safely exercise at controlled sizes. Never infer an MTU from a random public site that may rate-limit, terminate at a CDN, or choose another return path.

Watch for missing control messages

Run a packet capture in one terminal while reproducing the failing transfer in another. This broad filter is intentionally readable across IPv4 and IPv6; narrow it by host after you confirm the traffic.

sudo tcpdump -ni any -vv 'icmp or icmp6'

Look for IPv4 fragmentation-needed or IPv6 Packet Too Big messages that name a next-hop MTU. Their presence proves the path is giving feedback; repeated loss despite visible feedback may instead indicate local firewall, namespace, ECMP, or application handling. Their absence does not prove a black hole by itself, but absence combined with a repeatable size boundary is strong evidence.

Cloudflare’s PMTUD incident analysis shows why valid ICMP feedback can be lost or delivered to the wrong place in tunneled and ECMP networks. Preserve a short capture around the failure rather than running indefinite packet collection.

Choose the repair that matches the failure

Apply the narrowest repair that covers the affected traffic. MTU, MSS, ICMP policy, and TCP probing are not interchangeable controls.

Repair PMTU feedback when you control the filter

If a VPS firewall drops every ICMP or ICMPv6 message, allow the control traffic required for PMTUD through the applicable input/forward path. Do not disable the firewall, and do not assume that allowing Echo Request alone repairs PMTUD. IPv4 needs Destination Unreachable/fragmentation-needed feedback; IPv6 needs Packet Too Big.

Firewall syntax varies across nftables, iptables, UFW, firewalld, cloud security groups, and upstream DDoS filters. Inspect counters before and after a test, document the exact rule owner, and preserve an independent session. If the drop occurs inside an ISP, client network, or third-party tunnel you cannot change, move to a measured endpoint control instead of widening unrelated rules.

Set WireGuard MTU for traffic-wide protection

Derive an initial ceiling from the smallest proven outer PMTU required by that interface:

outer IPv4: inner ceiling = measured outer PMTU - 60
outer IPv6: inner ceiling = measured outer PMTU - 80
mixed paths: choose the lowest proven ceiling, then validate

Suppose the required outer IPv6 path reliably carries 1460 bytes. The initial inner ceiling is 1380. Test that value at runtime first; it will revert when the interface is recreated.

sudo ip link set dev wg0 mtu 1380
ip -details link show dev wg0

If real TCP, UDP, and IPv6 tests pass in both directions, persist the measured value under [Interface] in /etc/wireguard/wg0.conf:

[Interface]
MTU = 1380

The number above is an example derived from a measured 1460-byte outer IPv6 path, not a recommendation for every tunnel. Back up the configuration with restrictive permissions before editing. Do not run wg-quick down wg0 from the only session traveling through wg0; use runtime ip link testing and schedule any interface recreation behind console access.

Clamp MSS only for the routed TCP case

TCP MSS clamping changes the maximum segment advertised during SYN negotiation. It can help a VPS that forwards TCP between wg0 and another interface when remote endpoints do not learn the path limit. It does not fix UDP, does not change already established TCP flows, and should not conceal a correctable ICMP policy problem.

For a temporary iptables-based experiment, adapt interface names and capture the current rules first:

sudo sh -c 'umask 077; iptables-save > /root/iptables-before-wg-mtu-test.rules'

sudo iptables -t mangle -I FORWARD 1 -o wg0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
sudo iptables -t mangle -I FORWARD 1 -i wg0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

Verify that both temporary rules are present before opening new TCP connections:

sudo iptables -t mangle -S FORWARD

If the result is wrong, remove the exact inserted rules:

sudo iptables -t mangle -D FORWARD -i wg0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
sudo iptables -t mangle -D FORWARD -o wg0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

Persistence belongs in the server’s existing firewall manager only after new TCP connections prove the change. nftables-native systems should use an equivalent reviewed rule rather than maintaining a second firewall authority.

Reserve TCP MTU probing for a scoped fallback

Linux exposes net.ipv4.tcp_mtu_probing. Current kernel documentation defines mode 0 as disabled, mode 1 as enabling probing when an ICMP black hole is detected, and mode 2 as always enabled. Mode 1 can help locally terminated IPv4 TCP when PMTU feedback cannot be repaired, but it does not solve forwarded UDP or provide one correct WireGuard interface ceiling.

sysctl net.ipv4.tcp_mtu_probing
sudo sysctl -w net.ipv4.tcp_mtu_probing=1

# Roll back to the exact value recorded above if the test fails.
sudo sysctl -w net.ipv4.tcp_mtu_probing=<PREVIOUS_VALUE>

Treat a host-wide sysctl as change-controlled configuration. Do not make mode 2 a reflexive “performance” setting.

Run one reversible change window

Choose one variable: ICMP policy, runtime wg0 MTU, MSS clamp, or TCP probing. Changing several at once may restore traffic but destroys the diagnosis.

Preserve the return path

Before the experiment:

  1. open provider console or a second session outside the tunnel;
  2. record ip link show wg0, wg show, routes, rules, and firewall counters;
  3. copy /etc/wireguard/wg0.conf into a root-only backup location;
  4. write the exact rollback command beside the change;
  5. confirm another operator knows the test window when the tunnel carries production traffic.

Teams whose only goal is private shell administration may prefer private administration through Tailscale SSH instead of owning a routed WireGuard gateway. That is an architecture choice, not an MTU repair for deployments that genuinely need site-to-site or application routing.

Reopen flows after each change

Existing TCP sessions retain negotiated MSS and cached path state. Start new SSH, HTTPS, and application connections after the change. For routed clients, test from the client itself rather than only from the VPS. Compare the same file, endpoint, direction, and packet-size boundary captured before the experiment.

If an MTU decrease restores only TCP while UDP still fails, MSS negotiation or application behavior may be influencing the result. If both protocols recover at the same size boundary, the interface/underlay explanation becomes stronger. If nothing changes, roll back immediately and revisit routing, NAT, AllowedIPs, offload, or application-layer causes.

Validate protocols, not just ping

A successful ping closes only the ICMP portion of the investigation. Release the change when every traffic class the tunnel actually carries passes a representative test.

Build a small acceptance set

  • TCP: open a new SSH or HTTPS connection and transfer enough data to exceed one packet. Confirm no long retransmission stall.
  • UDP: exercise an owned DNS, voice, monitoring, or application path. MSS clamping cannot help this test.
  • IPv4 and IPv6: test both when both are routed. Do not infer IPv6 health from IPv4.
  • Forwarded traffic: run from a real client behind the VPS when FORWARD rules are involved.
  • Both directions: upload and download can follow different constraints.
  • Boundary probes: repeat the largest known-good packet and the controlled failing boundary to confirm the new ceiling is understood.
  • Counters: compare wg show wg0 transfer, interface drops/errors, firewall rule counters, and a short packet capture.

Avoid benchmarking during an unrelated traffic spike. MTU repair is about reliable delivery first; throughput tuning comes after correctness. An unnecessarily low MTU may appear stable while increasing packet overhead, so retain the measured ceiling and retest after a peer ISP, cloud route, or encapsulation layer changes.

For teams that do not want to own recurring route, firewall, and packet-capture work, assign that responsibility through managed hosting services with the tunnel topology and rollback boundary documented. Management does not remove the need for evidence; it gives the evidence an accountable owner.

Close the incident with an evidence record

Do not close a WireGuard MTU incident with “set 1380 and it worked.” Record which path was constrained, how it was measured, which traffic classes were affected, what changed, and how the result was proved.

A useful closure note contains the UTC window, peer and interface identifiers, outer IPv4/IPv6 PMTU observations, former and final wg0 MTU, visible ICMP/PTB evidence or its absence, transport tests, firewall/sysctl changes, rollback command, and the next retest trigger. Redact secrets and private addressing before the note leaves the operations team.

The final answer should be specific: “Outer IPv6 PMTU was 1460, so wg0 was tested and set to 1380; new TCP, UDP, IPv6, and forwarded-client flows passed in both directions; the prior value and rollback command remain in the change record.” That conclusion is far stronger than a universal MTU recipe because another operator can reproduce it—and recognize when a future route no longer matches it.

Leave a Reply

Your email address will not be published. Required fields are marked *