Part 1 ended with four wrong answers and a set of conclusions I was reasonably confident about. I have another Windows workstation, so I ran the same test from it the following day, and then moved it through every router path in turn. Three of those conclusions didn’t survive the afternoon.
The method is unchanged: curl -4, 1 GB per stream, single then four in parallel, CPU sampled across every transfer, and a control taken with the tunnel switched off. One addition. On the client-side tunnel I read the tunnel adapter’s byte counter before and after each run, which is a harder path proof than asking an external service what address it sees. Every client run showed 106% of payload crossing the interface, the extra 6% being WireGuard encapsulation. You can’t fake that by accident.
Five paths, one control
Everything below is measured against the same endpoint, a 1 GB file on an independent local host. Mixing endpoints between rows is one of the easier ways to publish nonsense, so I did not.
| Path | Single stream | 4 parallel | CPU during transfer |
|---|---|---|---|
| Router OpenVPN (IPv4) | 86 Mbps | 142 Mbps | router, not measured |
| Router WireGuard (IPv4) | 625 Mbps | 813 Mbps | router, not measured |
| Router IPv6 tunnel | 614 Mbps | 837 Mbps | router, not measured |
| Client WireGuard | 1,759 Mbps | 1,783 Mbps | 14.0% of 32 threads (Ryzen 9 9950X) |
| Direct (control) | 3,493 Mbps | 3,297 Mbps | 10.1% of 32 threads |
The tunnel I didn’t know I had
I started by putting the new machine behind the router’s OpenVPN tunnel and checking that both address families actually egressed through it. Both did. IPv4 came out at a hide.me local address, IPv6 came out at another hide.me local address, and traceroute showed tunnel hops on both. Question answered, or so it looked.
Then I measured them. IPv4 managed 86 to 152 Mbps depending on endpoint. IPv6 managed 646 to 662. Same router, same provider, same session, supposedly the same tunnel, and a factor of five between them.
One OpenVPN daemon cannot be slow and fast at the same time. If two numbers that far apart both come out of the same tunnel, it is not the same tunnel.
Part 1 already had the test for this. Run both at once and see whether they add up. Two streams of each family, simultaneously: IPv4 took 104 Mbps, IPv6 took 649, combined 754. Neither degraded. They were not sharing anything.
The confirmation came when I moved the machine to the router’s WireGuard tunnel. The IPv4 gateway changed, and IPv4 went from 86 Mbps to 625. IPv6 didn’t move at all: same gateway, same exit address, same path, same throughput. I changed one tunnel and the other one sat there unaffected, because it was never the one I was changing.
Half a VPN was about finding that half my traffic had never been inside the tunnel. That is fixed. What I hadn’t checked was whether the half that came back joined the same tunnel. It didn’t, and for however long that has been true, my IPv6 has been five times faster than my IPv4 for no reason I chose.
OpenVPN against WireGuard, this time properly controlled
Part 1 put WireGuard 3.4× ahead of OpenVPN. That comparison had a hole in it that I didn’t see at the time: nothing guaranteed the two runs used the same exit server, and the exit turns out to matter more than almost anything else.
This time the exit address was identical across both runs, an hour apart, same endpoints, same machine. The only variable was the protocol.
| Test | OpenVPN | WireGuard | Ratio |
|---|---|---|---|
| Endpoint A, single stream | 86 Mbps | 625 Mbps | 7.3× |
| Endpoint B, single stream | 152 Mbps | 695 Mbps | 4.6× |
| Endpoint A, 4 parallel | 142 Mbps | 813 Mbps | 5.7× |
| Endpoint B, 4 parallel | 167 Mbps | 716 Mbps | 4.3× |
Between 4.3× and 7.3×, not 3.4×. This is the one conclusion from Part 1 that got stronger under a better test, and it is the only recommendation I would still make without qualification.
Two tunnels on one router do not aggregate
Part 1’s decisive test showed router WireGuard at 842 Mbps running concurrently with a client tunnel at 446, for a combined 1,288. More than either alone. I concluded that tunnels aggregate, and that adding tunnels was the only lever that moves the tunnel tax.
That test had the second tunnel on a different machine. Here are the same measurements with both tunnels on the router.
In the first round IPv4 was crippled by OpenVPN, so it only claimed about 100 Mbps and IPv6 took the rest. That looked like aggregation. In the second round both families were on WireGuard tunnels, both individually capable of roughly 650 Mbps alone, and they each ended up with about half of that. Neither round exceeded 800.
So the router has a total VPN ceiling somewhere around 800 to 860 Mbps, and it gets divided according to what each tunnel is individually capable of pulling. Two tunnels on the same box share one budget. Two tunnels on different boxes do not. Part 1’s “more tunnels” lever works across machines, which is a much less convenient thing than what I wrote.
The client tunnel won this time
1,759 Mbps on a single stream, 1,783 across four. That’s 2.4× the router’s best and 3.3× the client machine in Part 1, on the same provider and the same line.
Stream count barely matters: one gives 1,759, four give 1,783, eight give 1,827. A single stream already saturates it, which is nothing like the router’s behaviour. The ceiling is still real and still per-tunnel — loading two independent endpoints simultaneously through the one tunnel produced 967 and 900 Mbps for a combined 1,867, when each alone reaches about 1,750. They split one budget. It just happens to be a 1.8 Gbps budget rather than an 850 Mbps one.
What sets that ceiling is still open. The client CPU is idle, two independent endpoints agree to within 2%, stream count doesn’t move it, and the line has more to give. That leaves the provider’s capacity at this particular exit, or the Windows WireGuard datapath. One number to chase: the tunnel sustains roughly 158,000 packets per second at its 1,392-byte MTU while the direct path pushes 291,000 at 1,500 bytes. If the limit is packet rate rather than bit rate, that is where to look. Hypothesis, not result.
The exit address also changed on its own, mid-session, without being asked.
More wrong answers
| What I concluded | Why it was wrong |
|---|---|
| “The provider caps a tunnel at 500–850 Mbps” | Generalised from the exits I happened to land on one evening. A different machine on a different exit holds 1,759 Mbps on a single stream, more than double the top of that range. hide.me rotates exits by itself. The underlying claim — the provider is the constraint, and it varies by exit — was right. The range I put next to it was the part people would remember, and it was far too narrow. |
| “I am running one VPN” | Never checked whether both address families used the same tunnel. They did not. One was five times faster than the other, and had been for as long as this configuration has existed. |
| “1.1.1.1 is a reasonable traceroute target” | It was the single destination on my router carrying a policy exception. One trace to it and I nearly published that IPv4 was leaking. |
| “Any speed test endpoint will do” | Twelve of fourteen candidates were unusable from a VPN exit. Cloudflare stops serving above about 50 MB, several others time out entirely. Depending on which host I had trusted, this machine’s tunnel tax would have read anywhere between 40% and 91%. |
Same shape as the four in Part 1. Every one of them is a component measured accurately and then reported as a fact about the system.
What I actually concluded, revised
Move off OpenVPN. Between 4.3× and 7.3× on the same router to the same exit server. If you change one thing after reading either of these posts, this is it.
The client tunnel is not slower. I said don’t bother. On this machine it is 2.4× the router, and the difference between the two client machines is the exit, not the silicon. The speed argument is gone. The operational ones stand, and they are why I am staying put: a client tunnel covers exactly one machine, needs its own config, and brings its own kill switch. On this host that kill switch blocks the LAN as well as the internet, so while the tunnel is up the machine can’t reach anything on its own network. A router tunnel doesn’t do that to you.
More tunnels aggregate, but only across machines. Two on one router share roughly 800 Mbps between them. If you were planning to stack tunnels on the router to buy throughput, that doesn’t work.
Check which tunnel your traffic is in, per address family. Not whether the VPN is on. Which tunnel. I had two running for months with a five-fold gap between them and no idea, and I only found out because a number looked wrong and I went after it instead of averaging it away.
Measure CPU next to throughput. Carried over from Part 1, still the cheapest way to tell a slow component from a slow supplier, still the measurement that stops you blaming the wrong thing.
Test host: Ryzen 9 9950X, 10 GbE, Windows 11, hide.me client (WireGuard, MTU 1392). Router: ASUS RT-BE88U, Asuswrt-Merlin 3006.102.8_4, quad-core ARM. Provider: hide.me, exits in Singapore. Endpoints: 1 GB files on Linode (endpoint A, independent) and DataPacket (endpoint B, cross-check; note this is the same network family as the VPN exit, so treat it as the friendlier of the two). Single-stream figures are medians of three runs; parallel figures are sums of per-stream throughput.
Client-tunnel runs were verified by reading the tunnel adapter’s byte counter, which showed 106% of payload crossing the interface. Router-tunnel runs have weaker proof — traceroute and exit address only — because the tunnel isn’t on this machine, and router CPU couldn’t be sampled for the same reason. The direct control is endpoint-limited: the best public host I could find managed 3,493 Mbps single-stream with the tunnel off, while speedtest.net reaches 8.4 Gbps on the same line by fanning out across servers. Treat every figure here as a ratio rather than a constant.