OpenVPN: Why It Feels Slow (And the Settings That Make It Fly)

Was this helpful?

Some VPN slowness is real; some is optical illusion; most is self-inflicted. You turn on OpenVPN, your speed test drops from “fine” to “why am I paying for fiber,” and suddenly everyone is an amateur network engineer.

OpenVPN can move serious traffic. It can also quietly sabotage you with a single mismatched MTU, a TCP-over-TCP faceplant, or a CPU pinned at 100% doing encryption in one lonely thread. Let’s diagnose it like adults: find the bottleneck, verify it with commands, then tune only what you can justify.

What “slow” actually means in OpenVPN

“Slow” is a grab bag. You need to name the pain before you touch settings:

  • Low throughput: big downloads crawl, backups time out, CI artifacts take forever.
  • High latency: SSH feels sticky, RDP stutters, VoIP drops words.
  • Jitter: it’s fine until it isn’t; interactive workloads feel haunted.
  • Connection instability: reconnect loops, TLS renegotiation churn, random stalls.

OpenVPN performance is not a single knob. It’s a pipeline: user-space crypto + kernel tunneling + routing/NAT + the real network. Any part can become the limiter. So we start with a quick triage, then drill down.

Fast diagnosis playbook (first/second/third checks)

First: determine if you’re CPU-bound or network-bound

  1. Check server CPU saturation during a transfer. If a single core is pegged, you’re likely crypto/user-space bound.
  2. Check link quality (loss, retransmits, MTU issues). If you see fragmentation or retransmits, you’re likely path/MTU bound.
  3. Check protocol choice. TCP over TCP is the classic “works in testing, dies in production” scenario.

Second: validate MTU/MSS correctness

  1. Look for ICMP “Frag needed” blackholes.
  2. Confirm you’re not fragmenting inside the tunnel.
  3. Set mssfix/tun-mtu conservatively and verify with packet captures.

Third: check buffers, queues, and scheduling

  1. Socket buffers and kernel queue disciplines.
  2. OpenVPN options like sndbuf/rcvbuf, and OS limits like net.core.rmem_max.
  3. TUN/TAP queue behavior and multi-queue where applicable.

If you do only one thing: prove the bottleneck with measurements. Tuning without measurement is just making your future self miserable.

How OpenVPN moves packets (and where it bleeds performance)

OpenVPN is mostly user-space. That’s both its superpower and its tax. Packets travel like this:

  1. App generates traffic (TCP/UDP).
  2. Kernel routes it into the TUN interface.
  3. OpenVPN reads from TUN in user-space.
  4. OpenVPN encrypts/authenticates, adds overhead (headers, IVs, tags).
  5. OpenVPN writes the encapsulated packets to a UDP or TCP socket.
  6. The internet does internet things: NAT, loss, reordering, MTU quirks.
  7. On the other side: reverse the process.

The performance implications are predictable:

  • Context switches: user-space read/write loops add overhead.
  • Single-thread hot spots: one process can become your choke point.
  • Crypto cost: depends on cipher, CPU features, and packet size distribution.
  • Encapsulation overhead: reduces effective MTU and can trigger fragmentation.
  • TCP behavior: if you tunnel TCP inside TCP, congestion control gets… philosophical.

One quote that belongs on every VPN performance runbook: paraphrased idea from Jim Gray: “Measure, then decide; performance guesses are usually wrong.” That’s the job.

Joke #1: Tuning OpenVPN without metrics is like optimizing a database by staring at it harder. The database will not respect your confidence.

Interesting facts and historical context (because it explains the weird bits)

  • OpenVPN’s first public releases were early 2000s, built for portability and correctness when “VPN appliance” often meant “mystery box.”
  • User-space design was deliberate: it avoided kernel module pain across OSes and made audits more approachable, at the cost of extra copies/context switches.
  • TUN/TAP abstractions came from Unix networking history; TUN (L3) is usually faster and simpler than TAP (L2) unless you truly need Ethernet bridging.
  • UDP mode became the default recommendation because TCP mode magnifies latency and retransmission pathologies under loss.
  • “TLS auth” / “tls-crypt” style controls evolved partly as a practical response to internet background noise and active probing.
  • AES-NI changed the game for OpenVPN throughput on x86: suddenly encryption wasn’t always the bottleneck—packet processing often was.
  • ChaCha20-Poly1305 rose in popularity because it performs well on devices without AES acceleration (many small routers and some VMs).
  • Data channel offload isn’t OpenVPN’s core story the way it is for some kernel-based VPNs; OpenVPN wins on maturity and flexibility, not zero-copy bravado.
  • Many “OpenVPN is slow” reports are actually MTU blackholes created by networks that block the ICMP needed for Path MTU Discovery.

The usual bottlenecks: CPU, MTU, buffers, routing, and bad defaults

1) CPU-bound encryption (or, more often, user-space packet churn)

If one OpenVPN process is doing all the work, you can saturate a single core long before saturating a 1 Gbps link—especially with many small packets (think interactive traffic, chatty APIs, DNS, RDP, SMB metadata). Crypto is expensive, but so is “just moving packets around” in user space.

What makes it worse:

  • Running OpenVPN in a small VM with constrained CPU scheduling.
  • Using a cipher that’s slow on your CPU (or missing hardware acceleration).
  • High packet rates: throughput may look mediocre even if Mbps isn’t huge.

2) MTU/MSS mismatch and fragmentation

Encapsulation steals bytes from your payload. If your inside traffic assumes 1500-byte MTU and your tunnel path can only carry, say, 1472 bytes (or less), you get fragmentation. Fragmentation can be merely inefficient, or it can be catastrophic when ICMP is blocked and PMTUD breaks.

The pattern is classic:

  • Small requests work.
  • Large downloads stall, especially over TCP.
  • Some sites work, others mysteriously hang.

3) TCP-over-TCP meltdown

Tunneling TCP traffic through an OpenVPN tunnel running over TCP is the performance equivalent of stacking two congestion controllers and asking them to coordinate using vibes. Under loss, both layers retransmit and back off, and latency balloons. It can “work” on perfect lab networks, then collapse on real-world links.

4) Buffers and queueing (aka “latency is a storage problem with packets”)

Buffers exist to smooth bursts. Too small, you drop. Too big, you bufferbloat and add latency. OpenVPN has its own socket buffers, and the OS has limits. You need sane sizing and sane queue disciplines, especially for broadband/long-fat networks.

5) Routing/NAT mistakes and asymmetric paths

OpenVPN can be fast while your routing is slow. If traffic takes a hairpin path through a firewall, or your NAT rules force conntrack churn, you’ll blame the VPN because that’s the visible change. Don’t. Follow the packets.

6) The quiet killer: DNS and name resolution

Sometimes the tunnel is fine but DNS over the tunnel is not. Latency spikes, retries, broken split-DNS, or resolvers you shouldn’t be using. Users report “VPN is slow” when what they mean is “every page waits on DNS.”

Hands-on tasks: commands, outputs, and decisions (12+)

These tasks are meant to be run on Linux servers/clients. Adjust interface names as needed. Each one includes what the output means and what decision you make from it.

Task 1: Confirm OpenVPN mode, cipher, and whether you’re on UDP

cr0x@server:~$ sudo ss -lntup | awk 'NR==1 || /openvpn/'
Netid State  Recv-Q Send-Q Local Address:Port Peer Address:Port Process
udp   UNCONN 0      0      0.0.0.0:1194      0.0.0.0:*     users:(("openvpn",pid=1324,fd=6))

Meaning: You’re listening on UDP/1194. Good default for performance.

Decision: If you see tcp here, be suspicious. Only use TCP if you absolutely must traverse hostile networks.

Task 2: Check server CPU saturation during a transfer

cr0x@server:~$ top -b -n 1 -o %CPU | head -n 12
top - 10:21:33 up 31 days,  4:02,  1 user,  load average: 1.92, 1.41, 1.22
Tasks: 162 total,   1 running, 161 sleeping,   0 stopped,   0 zombie
%Cpu(s):  8.2 us,  1.0 sy,  0.0 ni, 90.5 id,  0.2 wa,  0.0 hi,  0.1 si,  0.0 st
MiB Mem :  7986.4 total,  2141.2 free,  1680.0 used,  4165.2 buff/cache
MiB Swap:  2048.0 total,  2048.0 free,     0.0 used.  5789.1 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
 1324 nobody    20   0  178248  18624   7512 R  98.7   0.2  52:14.11 openvpn

Meaning: OpenVPN is pinning a core. You’re probably CPU/user-space bound.

Decision: Consider faster ciphers for your CPU, ensure AES-NI is available, reduce packet rate issues, or scale out (more instances, more tunnels, or different VPN tech if you need multi-gigabit).

Task 3: Verify hardware crypto capability (AES-NI) on x86

cr0x@server:~$ lscpu | egrep 'Model name|Flags' | head -n 2
Model name:                           Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz
Flags:                                fpu vme de pse tsc msr pae mce cx8 apic sep mtrr ... aes ...

Meaning: aes flag present; AES acceleration likely available.

Decision: Prefer AES-GCM on AES-NI capable CPUs. If absent (common on tiny ARM devices), consider ChaCha20-Poly1305 (if your OpenVPN build supports it).

Task 4: Identify packet loss and retransmits on the physical interface

cr0x@server:~$ ip -s link show dev 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
    RX:  bytes packets errors dropped  missed   mcast
    9812345678 8123456      0   12890       0       0
    TX:  bytes packets errors dropped carrier collsns
    8765432109 7345678      0    4321       0       0

Meaning: Drops on RX/TX can indicate congestion, driver issues, or buffer pressure.

Decision: If drops climb during VPN load, tune queue disciplines, increase buffers carefully, or fix NIC/virtio settings. Drops are not “normal.” They are a clue.

Task 5: Check UDP receive errors at the kernel level

cr0x@server:~$ netstat -su | egrep -i 'packet receive errors|receive buffer errors|ignored|InDatagrams'
    1892345 packets received
    0 packet receive errors
    0 receive buffer errors
    1892345 InDatagrams

Meaning: If receive buffer errors increments, the kernel is dropping UDP because buffers are too small or CPU can’t keep up.

Decision: Increase OS socket buffer limits and consider OpenVPN rcvbuf/sndbuf with push (but don’t blindly set them to “infinite”).

Task 6: Measure real tunnel throughput with iperf3 (don’t trust speed tests)

cr0x@server:~$ iperf3 -s
-----------------------------------------------------------
Server listening on 5201 (test #1)
-----------------------------------------------------------
cr0x@server:~$ iperf3 -c 10.8.0.1 -P 4 -t 15
Connecting to host 10.8.0.1, port 5201
[SUM]   0.00-15.00  sec   620 MBytes   347 Mbits/sec  sender
[SUM]   0.00-15.00  sec   616 MBytes   344 Mbits/sec  receiver

Meaning: You have a baseline. Parallel streams help reveal if you’re hitting single-flow congestion limits.

Decision: If one stream is slow but multiple streams are fine, look at TCP windowing/latency and congestion control, not raw bandwidth.

Task 7: Detect MTU blackhole with ping and “do not fragment”

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

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

Meaning: Your path MTU is smaller than 1500 payload expectation. Over a tunnel, it’ll be smaller still.

Decision: Set tun-mtu and mssfix to avoid fragmentation. Don’t guess; find the largest working size along the tunnel path.

Task 8: Check effective MTU on the tunnel interface

cr0x@server:~$ ip link show dev tun0
5: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN mode DEFAULT group default qlen 500
    link/none

Meaning: MTU is 1500 on tun0. That might be wrong depending on encapsulation and path.

Decision: If you see fragmentation or stalls, lower tun-mtu (and clamp MSS). A common sane starting point is 1500 minus overhead; many operators land around 1400–1450 depending on environment.

Task 9: Observe actual fragmentation with tcpdump

cr0x@server:~$ sudo tcpdump -ni eth0 udp port 1194 -vv -c 10
tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
10:25:11.123456 IP (tos 0x0, ttl 64, id 54321, offset 0, flags [+], proto UDP (17), length 1514)
    203.0.113.10.1194 > 198.51.100.20.51123: UDP, length 1486
10:25:11.123457 IP (tos 0x0, ttl 64, id 54321, offset 1480, flags [none], proto UDP (17), length 54)
    203.0.113.10.1194 > 198.51.100.20.51123: UDP, length 26

Meaning: The flags [+] and offsets indicate fragmentation.

Decision: Lower tunnel MTU and use MSS clamping. Fragmentation is a tax; blackholed fragmentation is an outage.

Task 10: Check for TCP retransmits (symptom of loss or MTU trouble)

cr0x@server:~$ ss -ti dst 10.8.0.2 | head -n 30
ESTAB 0 0 10.8.0.1:5201 10.8.0.2:51044
	 cubic wscale:7,7 rto:204 rtt:45.2/3.1 ato:40 mss:1360 pmtu:1400 rcvmss:1360 advmss:1360 cwnd:26 bytes_sent:12345678 bytes_retrans:23456 bytes_acked:12320000

Meaning: bytes_retrans climbing indicates retransmissions; note pmtu and mss values too.

Decision: If retransmits spike, fix loss first (Wi-Fi, LTE, overloaded links) and fix MTU second. Don’t “optimize” ciphers while the path is on fire.

Task 11: Verify routing: is traffic actually going through the tunnel?

cr0x@server:~$ ip route get 8.8.8.8
8.8.8.8 via 10.8.0.5 dev tun0 src 10.8.0.1 uid 0
    cache

Meaning: This destination routes via tun0. Good.

Decision: If it routes out eth0 when you expect tun0, you have a policy routing or pushed-route problem—users may be hairpinning, leaking, or split-routing unintentionally.

Task 12: Confirm NAT/forwarding for site-to-site or “push redirect-gateway” setups

cr0x@server:~$ sudo sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1
cr0x@server:~$ sudo nft list ruleset | sed -n '1,120p'
table inet filter {
  chain forward {
    type filter hook forward priority filter; policy drop;
    iifname "tun0" oifname "eth0" accept
    iifname "eth0" oifname "tun0" ct state established,related accept
  }
}
table ip nat {
  chain postrouting {
    type nat hook postrouting priority srcnat; policy accept;
    oifname "eth0" ip saddr 10.8.0.0/24 masquerade
  }
}

Meaning: Forwarding is enabled and NAT exists for tunnel clients going out via eth0.

Decision: If forwarding is off or rules drop, you’ll see “VPN connects but no internet” or weird slowness due to asymmetric return paths.

Task 13: Check socket buffer maximums (OS limits)

cr0x@server:~$ sysctl net.core.rmem_max net.core.wmem_max net.ipv4.udp_rmem_min net.ipv4.udp_wmem_min
net.core.rmem_max = 212992
net.core.wmem_max = 212992
net.ipv4.udp_rmem_min = 4096
net.ipv4.udp_wmem_min = 4096

Meaning: Defaults are conservative. On high-BDP links, this can cap throughput or cause drops.

Decision: Raise them cautiously (and monitor). If you raise OpenVPN buffers but OS max is tiny, your setting won’t stick.

Task 14: Validate queue discipline (bufferbloat control)

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 ecn

Meaning: fq_codel is generally friendly for latency under load.

Decision: If you see a huge pfifo_fast or massive buffers on WAN egress, consider moving to fq_codel or cake (where available) to tame latency spikes.

Settings that make it fly (and what to avoid)

Pick UDP unless you have a real reason not to

UDP is not “faster because it’s UDP.” It’s faster because you avoid TCP-over-TCP congestion collapse and you let the inner TCP sessions do their job without being wrapped in another reliability layer.

Do: proto udp for most deployments.

Avoid: proto tcp as a default. Use it for captive portals and locked-down networks, and accept the performance hit.

Use TUN (routed) unless you truly need TAP (bridged)

TAP drags Ethernet semantics into your life: broadcasts, ARP noise, and bigger frames. Sometimes you need it (legacy apps, L2 adjacency). Often you don’t.

Do: dev tun for site-to-site and client VPNs.

Avoid: dev tap when the only justification is “it was in a blog post.”

Get MTU/MSS right, then stop touching it

If you remember one tuning rule: avoid fragmentation inside the tunnel. Use mssfix to clamp TCP MSS so endpoints don’t send segments too large for the effective path MTU. Use tun-mtu to set the tunnel interface MTU if needed.

Typical server-side config snippets (illustrative, not universal):

  • mssfix: clamps TCP MSS; good for avoiding PMTUD issues.
  • tun-mtu: sets tunnel MTU; helps enforce no-fragment sizes.
  • fragment: generally avoid; fragmentation is usually a workaround for a broken path and can interact badly with modern networks.

Choose modern ciphers with an eye on your CPU

On x86 with AES-NI, AES-128-GCM is often a sweet spot. AES-256-GCM can be fine too, but don’t pay extra cycles for no benefit in your threat model. On devices without AES acceleration, ChaCha20-Poly1305 can perform better.

Also: cipher negotiation and compatibility matter. A “fast” cipher you can’t use everywhere is just future change risk.

Don’t oversize buffers blindly

Yes, bigger buffers can help throughput on high-latency links. They can also create seconds of queueing delay and make interactive traffic miserable. Think of buffers like storage writeback cache: useful until it becomes your incident.

Keepalive and renegotiation: don’t create self-inflicted jitter

Renegotiation and aggressive keepalives can cause periodic stalls. Set keepalives appropriate to NAT timeouts, and don’t churn TLS unnecessarily. If you see periodic “everyone freezes for a second,” check renegotiation intervals and CPU spikes.

Scale out when you hit single-process limits

OpenVPN can do multi-client well, but there are real ceilings per process depending on packet rate, crypto, and CPU. If you’re pushing big aggregate bandwidth, consider:

  • Multiple OpenVPN instances bound to different ports/IPs.
  • Client distribution across instances.
  • More CPU and faster cores (not just more RAM).
  • Re-evaluating whether a kernel-based VPN suits your needs better if you’re chasing multi-gigabit per-tunnel performance.

Joke #2: If you set every buffer to 16 MB, you didn’t tune the VPN. You built a latency museum and charged your users admission.

Three corporate mini-stories from the trenches

Mini-story 1: The incident caused by a wrong assumption

A mid-sized company rolled out OpenVPN to connect remote staff to internal tools. The pilot group loved it. The rollout went global. Then the ticket queue started to look like a denial-of-service attack, except the attackers were employees.

Symptoms were inconsistent: some users could load internal web apps fine, but downloading large files from an internal artifact store would hang. Others reported that only certain sites were broken, and the failures varied by hotel Wi‑Fi. Naturally, everyone blamed the OpenVPN server because it was the new thing.

The wrong assumption: “MTU will just work because Ethernet is 1500.” The network team had a path with a smaller MTU in the middle (a provider segment and a VPN-in-VPN hop), and ICMP “fragmentation needed” messages were being filtered. Path MTU Discovery couldn’t do its job. Small packets survived; larger ones died silently.

The fix was boring and immediate: clamp MSS and lower the tunnel MTU to a safe value across the fleet. Once they did, the “random hangs” disappeared. They later went back and fixed ICMP filtering, but the MTU/MSS settings were the real production-grade belt and suspenders.

Mini-story 2: The optimization that backfired

A different org wanted higher throughput for nightly data sync. Someone read that “bigger buffers improve performance” and cranked OpenVPN sndbuf/rcvbuf way up, plus increased kernel maxima. Bandwidth went up in a synthetic test. Celebration ensued.

Then the complaints started: VoIP quality dropped, terminal sessions felt delayed, and monitoring started flapping. Nothing was “down,” but everything felt worse. It was the kind of degradation that makes you question your career choices.

They’d created bufferbloat on the VPN egress. The nightly sync got more throughput by sitting on a mountain of queued packets, and the interactive traffic had to wait behind it. Under load, latency spiked from “fine” to “are we under attack?” There was no attack. Just enthusiastic buffering.

The rollback improved user experience instantly. The eventual solution was to use fair queueing on egress, keep buffers reasonable, and schedule bulk sync traffic with rate limiting so it didn’t bulldoze everything else.

Mini-story 3: The boring but correct practice that saved the day

A regulated enterprise ran OpenVPN for vendor access into a segmented environment. Nothing glamorous: strict firewall rules, explicit routes, and change windows that could fossilize a bug report.

The team did one unsexy thing consistently: they kept a baseline performance runbook with repeatable tests—iperf3 across the tunnel, MTU probes, CPU snapshots, and packet captures for the control and data channels. Every change request included “before” and “after” results.

One month, performance complaints spiked after a firewall policy update. Because they had baselines, they quickly proved the VPN server wasn’t the limiter: CPU was fine, no fragmentation, stable throughput on-server. Packet captures showed increased retransmissions and out-of-order delivery beginning at the perimeter.

It turned out a new inspection policy on the firewall was interacting badly with UDP flows at scale. They reverted just that policy and restored normal performance. Without baselines, the blame would have landed on OpenVPN (and the people who maintain it), and the fix would have taken weeks of misdirected tuning.

Common mistakes: symptom → root cause → fix

  • Symptom: “VPN connects, browsing okay, but large downloads hang.”

    Root cause: MTU blackhole / PMTUD broken; fragmentation needed messages blocked.

    Fix: Set mssfix and possibly lower tun-mtu; verify with ping -M do and tcpdump fragmentation checks.

  • Symptom: “Throughput is capped suspiciously low; CPU looks high.”

    Root cause: CPU-bound OpenVPN process; slow cipher on that CPU; high packet rate overhead.

    Fix: Prefer AES-GCM on AES-NI systems or ChaCha20 on non-AES; allocate faster cores; scale out instances.

  • Symptom: “Works in office, terrible on mobile or Wi‑Fi.”

    Root cause: Packet loss and reordering; TCP-over-TCP if OpenVPN runs over TCP; or aggressive MTU.

    Fix: Use UDP transport; clamp MSS; consider conservative MTU; ensure keepalives are appropriate for NAT.

  • Symptom: “Speed tests show high latency under load; calls stutter during big transfers.”

    Root cause: Bufferbloat from oversized buffers or bad qdisc.

    Fix: Use fq_codel/cake where possible; keep buffers sane; rate-limit bulk flows.

  • Symptom: “Only DNS is slow; everything feels delayed at page load.”

    Root cause: DNS resolvers pushed over the tunnel are slow/unreachable; split DNS misconfigured.

    Fix: Test resolver reachability/latency; push correct DNS; avoid hairpinning to a distant resolver.

  • Symptom: “VPN throughput fine for some clients, terrible for others.”

    Root cause: Client-side MTU/Wi‑Fi/LTE issues; different ISP NAT behavior; different routes to server POP.

    Fix: Test per-client MTU and loss; prefer nearer server endpoints; standardize client configs; measure path with mtr.

  • Symptom: “After enabling compression, speed got worse and CPU got hotter.”

    Root cause: Compression overhead on already-compressed traffic; security concerns; extra CPU in user-space.

    Fix: Disable compression for general internet traffic; only consider it in narrow, controlled cases with measurable benefit.

  • Symptom: “Intermittent stalls every N minutes.”

    Root cause: Rekey/renegotiation events causing CPU spikes; NAT timeouts causing brief outages; watchdog restarts.

    Fix: Inspect logs for renegotiations; adjust keepalive; ensure system resources stable; don’t set overly aggressive rekey intervals without cause.

Checklists / step-by-step plan

Step-by-step: make OpenVPN fast without making it fragile

  1. Establish a baseline: run iperf3 across the tunnel (1 stream and multiple streams), record RTT and retransmits.
  2. Confirm transport: use UDP unless you have a hard constraint requiring TCP.
  3. Check CPU: if one core is pinned, fix crypto choice/CPU allocation/scale-out before touching MTU.
  4. Fix MTU/MSS: probe MTU; set mssfix; adjust tun-mtu if needed; verify fragmentation disappears.
  5. Verify kernel drops: use ip -s link and netstat -su; if drops occur, address buffers/CPU/NIC.
  6. Validate routing: ensure routes are as intended; avoid hairpin paths; confirm return routing symmetry.
  7. Control queueing: verify qdisc; avoid bufferbloat; if you must prioritize, do it explicitly.
  8. Re-test: repeat baseline tests and compare. If you didn’t measure, you didn’t fix it.
  9. Document your “known good” config: include MTU assumptions, cipher choices, and OS sysctls used.
  10. Monitor continuously: CPU, packet drops, and tunnel uptime should be on dashboards, not in someone’s head.

Quick checklist: performance tuning do’s and don’ts

  • Do use UDP transport for performance.
  • Do clamp MSS to avoid fragmentation surprises.
  • Do verify AES-NI / CPU features and pick ciphers accordingly.
  • Do use fair queueing on WAN interfaces when possible.
  • Don’t run TCP-over-TCP unless you like debugging retransmits for sport.
  • Don’t “solve” throughput by inflating buffers until latency becomes a personality trait.
  • Don’t enable compression as a generic speed button.
  • Don’t tune in the dark—log, measure, then change one thing at a time.

FAQ

1) Why does OpenVPN feel slower than my raw internet connection?

Because you added encryption overhead, extra headers (smaller effective MTU), and user-space packet processing. Any one of those can become the cap.

2) Is UDP always faster than TCP for OpenVPN?

Not always, but usually more stable and higher-performing under loss. TCP transport is sometimes necessary for restrictive networks, but it’s a performance compromise.

3) What’s the single most common real-world cause of “OpenVPN is slow”?

MTU/PMTUD problems leading to fragmentation or blackholing. It creates the worst kind of problem: intermittent, path-dependent, and misattributed.

4) Should I enable compression to make OpenVPN faster?

Generally no. Most modern traffic is already compressed (HTTPS, video, downloads). Compression burns CPU and can introduce security concerns. Use it only if you can prove it helps your specific traffic mix.

5) How do I know if I’m CPU-bound?

During a transfer, if the OpenVPN process pins a core and throughput plateaus, you’re CPU/user-space bound. Confirm with top and compare throughput changes after cipher/CPU changes.

6) What MTU should I set for OpenVPN?

There’s no universal number. Start by measuring path MTU and then set mssfix to prevent TCP segments from exceeding the effective MTU. Many environments land around 1400–1450 for tunnel MTU, but measure your path.

7) Why do multiple iperf3 streams improve throughput?

One stream can be limited by TCP window growth and loss recovery. Multiple streams can better utilize bandwidth on high-latency paths. It’s also a hint: if parallel streams help, your issue isn’t raw bandwidth alone.

8) Can OpenVPN do gigabit speeds?

Yes, in some setups with strong CPUs and good tuning. But packet rate matters: lots of small packets can cap you before Mbps does. Past a point, scaling out or changing VPN technology may be cleaner.

9) Should I use TAP bridging for “better performance”?

No. TAP is for L2 requirements, not speed. It often increases overhead due to broadcast and ARP noise. Use TUN unless you need Ethernet adjacency.

10) Why is performance fine in the office but bad for remote users?

Because the internet is not one network. Remote users have different MTUs, NAT behaviors, loss rates, and routes. Measure from the user’s path and tune to survive hostile networks.

Conclusion: practical next steps

If OpenVPN feels slow, don’t start with a config séance. Start with evidence.

  1. Run iperf3 across the tunnel (single and parallel). Record throughput and RTT.
  2. Check CPU while the test runs. If a core is pinned, fix that first.
  3. Probe MTU and eliminate fragmentation. Clamp MSS. Verify with tcpdump.
  4. Look for drops and buffer errors in kernel stats. Fix queueing and buffer limits sanely.
  5. Re-test and document a “known good” configuration so your future incidents have fewer plot twists.

OpenVPN can be boringly fast when you treat it like a system, not a magic tunnel. Measure. Change one thing. Measure again. Repeat until the graphs stop yelling.

← Previous
The GPU Reset Bug Explained: Why IOMMU Isn’t Enough (and What Works)
Next →
High CPU ‘System’ Process? It’s Often a Driver — Here’s How to Prove It

Leave a comment