Benchmarks
Measured on 2026-10-25
| Dataplane | Runs in | Protocol and cipher |
|---|---|---|
| ranet3 | Userspace | ESP in UDP, AES-256-GCM |
| wireguard-go | Userspace | WireGuard, ChaCha20-Poly1305 |
| Tailscale | Userspace | WireGuard, ChaCha20-Poly1305 |
| NetBird | Userspace | WireGuard, ChaCha20-Poly1305 |
| WireGuard | Kernel | WireGuard, ChaCha20-Poly1305 |
| NetBird | Kernel | WireGuard, ChaCha20-Poly1305 |
| IPsec | Kernel (xfrm) | ESP in UDP, AES-256-GCM |
2 runs under saturation
Head to head
| Dataplane | Throughputhigher is better | CPU per Gbit/slower is better | Latency p99lower is better |
|---|---|---|---|
| wireguard-go user | 1.3-1.5x better | 0.8-0.9x better | 0.6-1.2x mixed |
| Tailscale user | 1.3-1.5x better | 0.8-0.9x better | 0.2-0.6x better |
| NetBird user | 1.3-1.5x better | 0.8-0.9x better | 0.2-1.4x mixed |
| WireGuard kernel | 2.0-2.7x better | 0.5-0.6x better | 1.4-4.8x worse |
| NetBird kernel | 2.1-2.6x better | 0.5-0.6x better | 1.6-6.4x worse |
| IPsec kernel | 2.2-4.0x better | 1.1-1.6x worse | 1.7-5.6x worse |
Each range is ranet3's result divided by the other dataplane's across the 6 tests below.
Kernel and userspace
Userspace dataplanes copy every packet into their own process and are usually expected to be slower than kernel ones.
| 1 stream, Gbit/s | One way | Both ways | Share of lineone way |
|---|---|---|---|
| Line limit (10 GbE) | 8.84 | 18.76* | 100% |
| Kernel, best of 3 | 4.43 | 6.17 | 50% |
| Userspace, wireguard-go | 6.87 | 10.60 | 78% |
| Userspace, Tailscale | 6.87 | 10.98 | 78% |
| Userspace, ranet3 | 8.79 | 15.81 | 99% |
* Plain TCP without tunnel
Kernel WireGuard stays at 4.0 to 4.4 Gbit/s one way.
Kernel IPsec stays at 3.5 to 4.5 Gbit/s because its single flow lands on 1 NIC receive queue.
Full results
| ranet3user | wireguard-gouser | Tailscaleuser | NetBirduser | WireGuardkernel | NetBirdkernel | IPseckernel | |
|---|---|---|---|---|---|---|---|
| 1 stream, out | 8.79 | 6.87 | 6.87 | 6.58 | 4.43 | 4.18 | 4.07 |
| 1 stream, in | 8.80 | 6.86 | 6.86 | 6.77 | 4.34 | 4.10 | 4.08 |
| 1 stream, both | 15.81 | 10.60 | 10.98 | 10.69 | 6.17 | 6.14 | 4.45 |
| 8 streams, out | 8.73 | 6.40 | 6.34 | 5.70 | 4.22 | 4.00 | 3.80 |
| 8 streams, in | 8.74 | 6.52 | 6.45 | 6.73 | 4.02 | 3.97 | 3.54 |
| 8 streams, both | 15.24 | 10.13 | 10.17 | 10.20 | 5.58 | 5.93 | 3.81 |
| ranet3user | wireguard-gouser | Tailscaleuser | NetBirduser | WireGuardkernel | NetBirdkernel | IPseckernel | |
|---|---|---|---|---|---|---|---|
| 1 stream, out | 0.926 | 1.070 | 1.102 | 1.038 | 1.569 | 1.630 | 0.670 |
| 1 stream, in | 0.914 | 1.081 | 1.086 | 1.039 | 1.577 | 1.652 | 0.677 |
| 1 stream, both | 0.857 | 1.026 | 0.968 | 0.996 | 1.529 | 1.511 | 0.541 |
| 8 streams, out | 0.927 | 1.205 | 1.111 | 1.167 | 1.788 | 1.825 | 0.864 |
| 8 streams, in | 0.931 | 1.171 | 1.121 | 1.163 | 1.783 | 1.849 | 0.849 |
| 8 streams, both | 0.913 | 1.088 | 0.982 | 1.058 | 1.542 | 1.554 | 0.683 |
In the "both" rows kernel IPsec carried only 4.45 and 3.82 of the 6 Gbit/s offered, and NetBird 5.84 at 8 streams.
| ranet3user | wireguard-gouser | Tailscaleuser | NetBirduser | WireGuardkernel | NetBirdkernel | IPseckernel | |
|---|---|---|---|---|---|---|---|
| 1 stream, out | 2.74 | 2.29 | 4.87 | 2.34 | 1.18 | 1.09 | 1.39 |
| 1 stream, in | 2.96 | 2.63 | 4.81 | 2.13 | 1.00 | 1.07 | 1.26 |
| 1 stream, both | 2.63 | 3.11 | 6.02 | 3.02 | 1.85 | 1.67 | 1.57 |
| 8 streams, out | 6.65 | 12.00 | 25.20 | 34.80 | 1.38 | 1.35 | 1.42 |
| 8 streams, in | 7.81 | 7.76 | 30.20 | 6.65 | 1.91 | 1.22 | 1.39 |
| 8 streams, both | 4.63 | 6.03 | 18.90 | 8.57 | 2.98 | 2.53 | 2.63 |
Setup
Hardware
- Hosts
- 2 x Dell PowerEdge C6420
- Processor
- Intel Xeon Gold 6130 with 16 cores and 32 threads
- Memory
- 192 GiB
- NIC
- Intel X710
- Connector
- SFP+
- Bandwidth
- 10 Gbit/s each way
- Switch
- Both hosts on the same Dell S5296F-ON
Software
- OS
- Debian 13
- Kernel
- Linux 6.12.111
- wireguard-go
- 0.0.20250522
- wireguard-tools
- 1.0.20260223
- Tailscale
- 1.104.2
- headscale
- 0.29.5
- NetBird
- 0.81.2
- iproute2
- 7.1.0
- iperf3
- 3.21
Method
- Traffic
- TCP with cubic at inner MTU 1400
- Streams
- 1 and 8
- Load
- Saturated for throughput and latency and 3 Gbit/s each way for CPU cost
- Direction
- One way in each direction and both ways at once
- Runs
- 2 runs of 10 seconds with the dataplanes interleaved, throughput and CPU cost averaged, RTT p99 over both runs' pings
- Paths
- Direct between the hosts for every dataplane, with no relay, checked on every run
- Control
- headscale for Tailscale and NetBird's own servers on one host, at most 0.002 cores during a run
- Outer IP
- IPv4, except where Tailscale and NetBird chose IPv6, whose header adds about 1.3% on the wire
- CPU cost
- Busy cores from /proc/stat summed over both hosts with every dataplane unpinned
- Line limit
- 8.84 Gbit/s of TCP payload one way after ESP in UDP framing and 18.76 Gbit/s both ways without a tunnel