The bug report from myself to myself read: "SSH works, small web pages load, apt update hangs forever, large file copies stall at exactly the same point every time." That very specific pattern, small things fine, big things dead, is MTU waving at you. It's just that MTU never introduces itself, so it took me longer than I'd like to admit to recognise the wave.
The setup was a new WireGuard tunnel between the homelab and a small VPS. Pings flew. Interactive SSH was perfect. Then anything that wanted to push a full-size packet, a big download, a git clone, a database dump, would get a few kilobytes in and freeze.
What's happening is that WireGuard wraps every packet with its own header, so the room left inside for your actual data shrinks. The default Ethernet MTU is 1500 bytes, but once you've paid for the tunnel overhead the real budget through the tunnel is more like 1420. A full-size 1500-byte packet no longer fits. Normally the network would fragment it or send back an ICMP "fragmentation needed" message telling the sender to use smaller packets. That mechanism is Path MTU Discovery, and it is delicate. Large packets often carry the "don't fragment" bit, so they can't simply be chopped up. And the ICMP message that's supposed to politely report the problem gets dropped by approximately every overzealous firewall on the internet.
So the sender keeps cheerfully transmitting 1500-byte packets that are too big to pass and never gets told. Small packets fit, so SSH and tiny pages work. Large transfers consist entirely of full-size packets, so they vanish into a black hole. The connection doesn't error, it just stops. Silent.
The diagnostic that confirms it is ping with a fixed payload and the don't-fragment flag set, walking the size down until packets start getting through:
ping -M do -s 1472 10.0.0.2 # fails: 1472 + 28 = 1500, too big for the tunnel
ping -M do -s 1392 10.0.0.2 # works: 1392 + 28 = 1420
When the big one fails and the smaller one succeeds, you've found your ceiling. The fix is to stop pretending the tunnel can carry 1500 bytes and tell it the truth:
[Interface]
MTU = 1420
Set that on the WireGuard interface, bring it back up, and the stalls disappear. The transfers that died at the same byte every time now run clean to the end.
There's a belt-and-braces option too. If you can't control the MTU everywhere, you can have the router clamp TCP's negotiated segment size down to fit the path, so connections agree to smaller packets at handshake time:
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
-j TCPMSS --clamp-mss-to-pmtu
MSS clamping only helps TCP, not UDP, but the things that hurt most here were TCP, so it earns its place.
The reason I'm writing this down is that I have diagnosed this exact problem at least four times across the years and started from scratch every single time. Tunnels, VLANs with jumbo frames misconfigured at one end, PPPoE links, a Docker bridge once. Different cause, same fingerprint every time: small traffic fine, big traffic dead, no error anywhere. So if you ever find yourself staring at a connection that works for everything except the things that matter, say the word "MTU" out loud before you do anything else. It'll save you an afternoon.