Skip to content
Go back

The MTU Problem Nobody Diagnoses Correctly

By SumGuy 4 min read
The MTU Problem Nobody Diagnoses Correctly

The Symptom: “It Works Sometimes”

Large file transfers work fine locally but timeout over VPN. Video calls drop during screen shares. Database backups fail randomly. You restart things and sometimes they work. You run the same command twice and the second time fails.

Classic MTU problem. Nobody thinks to check it because you inherited the network and it’s been like that for years.

What Is MTU Anyway?

MTU = Maximum Transmission Unit. It’s the largest frame your network can send in one packet. Standard Ethernet is 1500 bytes. Jumbo frames go to 9000. Your VPN might use 1280. Tunnel protocols (WireGuard, OpenVPN) add overhead.

When you send data bigger than the MTU, one of two things happens:

  1. The system fragments it (breaks it into smaller pieces) and reassembles it—slow but works
  2. The DF bit (Don’t Fragment) is set and the packet is dropped—silent failure, retry, timeout

Most VPNs set the DF bit. Most tunnels don’t advertise their actual MTU properly. Result: transfers work for small files, hang forever on large ones.

The Diagnosis: Path MTU Discovery

Find the actual MTU being used on a path:

Terminal window
# Linux: Use tracepath
tracepath -m 15 example.com 22
# Output shows where MTU drops:
# hop 1: [reached, mtu 1500]
# hop 2: [reached, mtu 1500]
# hop 3: [reached, mtu 1350] <-- MTU drops here
# hop 4: no reply

Or use ping with DF bit set:

Terminal window
# Find the largest packet that doesn't fragment:
ping -M do -s 1472 example.com
# 1472 = 1500 (MTU) - 28 (IP/ICMP headers)
# If this fails, try smaller:
ping -M do -s 1400 example.com
ping -M do -s 1300 example.com
ping -M do -s 1200 example.com

When it succeeds, you’ve found your path MTU. Most home networks max out around 1500. VPNs often drop to 1280 or less.

Check Your Local Interface MTU

Terminal window
ip link show

You’ll see something like:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
4: tun0: <POINTOPOINT,MULTICAST,UP,LOWER_UP> mtu 1280

Notice tun0 (VPN interface) has MTU 1280 while eth0 has 1500. When you’re on the VPN sending traffic, your effective MTU is 1280.

The Real Problem: Encapsulation Overhead

Your VPN or tunnel adds protocol overhead. Here’s what actually happens:

If you’re trying to send a 1500-byte packet through a tunnel with 40 bytes overhead, the tunnel has to fragment it. Fragments are slow and unreliable.

Solutions:

  1. Lower the MTU on the tunnel interface (quick fix):
Terminal window
sudo ip link set dev tun0 mtu 1420

This tells the system to never send packets larger than 1420 through the VPN. No fragmentation, no drops.

  1. Configure the VPN to advertise correct MTU (proper fix):

For OpenVPN in /etc/openvpn/client.conf:

mtu-test
fragment 1300

For WireGuard in /etc/wireguard/wg0.conf:

[Interface]
Address = 10.0.0.2/32
MTU = 1420

Test the Fix

Before and after:

Terminal window
# Before fix: large file stalls
scp largefile.tar.gz user@remotehost:/tmp/
# After fix: completes instantly
scp largefile.tar.gz user@remotehost:/tmp/

Or use iperf for network performance testing:

Terminal window
# On receiver:
iperf -s
# On sender (check throughput):
iperf -c receiver.host
# With incorrect MTU, you'll see low throughput and packet loss.
# After fix, throughput jumps 5-10x.

Real-World Debugging

Terminal window
# 1. Check if you can ping yourself over the tunnel:
ping -M do -s 1472 10.0.0.1 # Your tunnel IP
# 2. If it fails, binary search for working size:
ping -M do -s 1400 10.0.0.1
ping -M do -s 1350 10.0.0.1
ping -M do -s 1300 10.0.0.1 # <-- maybe works
# 3. That's your path MTU. Lower your interface MTU to leave headroom:
sudo ip link set dev tun0 mtu 1280
# 4. Test again:
ping -M do -s 1252 10.0.0.1 # 1280 - 28 byte headers
# 5. Try the large transfer again
scp largefile user@remote:/tmp/

The Permanent Fix

Edit /etc/systemd/network/tun0.network (or wherever your tunnel is defined):

[Match]
Name=tun0
[Link]
MTUBytes=1420

Reboot (or systemctl restart systemd-networkd) and it sticks.

Why This Stays Broken

Most people don’t think about MTU until transfers fail. By then they’re tired and just restart the VPN or the server. MTU issues are invisible when you’re using small files.

But the moment someone tries to transfer a database dump or a VM backup, the tunnel becomes a bottleneck and nobody traces it back to frame size.

Check your MTU once. Fix it once. Never debug this problem again.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it may appear here.


Previous Post
Gitea vs Forgejo vs GitLab CE: Self-Hosted Git Without the Existential Crisis
Next Post
Open WebUI vs LibreChat: Self-Hosted ChatGPT Alternatives Compared

Related Posts