Cloud Native Tips & Tricks

We benchmarked Hetzner's new network stack: 120k firewall connections and faster WireGuard®

Benchmark of Hetzner Cloud's new eBPF network stack against the standard stack: firewall connection limits, WireGuard® tunnels, throughput, packet rate, and latency.

Earlier this year we saw a demo of Hetzner’s new network stack at the Hetzner Summit, and we have wanted to test it ever since.

Cloudfleet operates thousands of Hetzner Cloud servers on behalf of our customers as Kubernetes nodes. Most of our engineering time on Hetzner goes into networking: keeping nodes reachable, firewalled, and load-balanced at a scale where small limits cause incidents. A change to how Hetzner moves packets between servers is a change to the platform our customers run on.

So when Hetzner announced hcnet, its new cloud network stack, we wanted numbers. hcnet is built on eBPF, compiles every Cloud Firewall into its own eBPF program, and already carries all Hetzner Load Balancers. For servers it is an opt-in beta: you switch a server in the Hetzner Console and can switch it back at any time. The announcement, and the discussion on r/hetzner, describe the functional changes but publish no performance figures.

We put identical servers on both stacks and measured throughput, packet rate, latency, connection setup, and firewall connection tracking. To our knowledge, this is the first independent benchmark of hcnet.

Key results

Every result below held on two separate pairs of hosts, each measured on both stacks.

Metric Standard stack hcnet Change
Firewall connection tracking 84,000-88,000, shared by IPv4 and IPv6 120,000 per address family 1.4x for IPv4, up to 2.7x with IPv4 + IPv6
Recovery after a full tracking table 44-64 s 0-3 s about 20x faster
WireGuard® tunnel throughput, 4 streams 3.1-4.0 Gbit/s 4.1-4.7 Gbit/s +18% to +39%
TCP request/response p99 latency, public IPv4 189-217 µs 165-172 µs -13% to -21%
TCP throughput, 1 stream 7.8-9.0 Gbit/s 7.5-8.8 Gbit/s no consistent change
TCP throughput, 8 streams into one VM, CCX23 15.6-18.1 Gbit/s 9.1-10.4 Gbit/s -39% to -50%
TCP throughput, 8 streams into one VM, CPX42 7.5-8.9 Gbit/s 3.5-4.1 Gbit/s about -55%

Change with hcnet on public IPv4, per host pair: WireGuard® throughput up 18 to 39 percent, p99 request latency 13 to 21 percent lower, new-connection p99 11 to 16 percent lower, single-stream TCP 6 to 10 percent lower, 8-stream TCP 44 to 46 percent lower

hcnet raises the connection tracking limit for servers behind a Cloud Firewall, recovers from a full table within 3 seconds, and makes WireGuard® tunnels faster. The one regression is bulk multi-stream TCP into a single VM, where the receiving vCPU becomes the bottleneck.

Test setup

How to tell which stack a server uses

The API does not expose the network stack. From inside the server, the most reliable signal is the IPv6 metadata endpoint, which only hcnet serves:

curl -g "http://[fe80::a9fe:a9fe%eth0]/hetzner/v1/metadata/instance-id"

On hcnet this returns the server ID. On the standard stack it times out.

Firewall connection tracking limits

Cloud Firewalls are stateful. The network tracks every connection so it can let response traffic back in. On the standard stack, Hetzner documents a limit of 80,000 tracked connections, shared between IPv4 and IPv6. hcnet raises this to 120,000 per address family.

We tested the limits with a firewall that only allows inbound TCP on the service ports. With that firewall, a connection can only succeed while the network has room to track it. The client then opened idle TCP connections in steps of 10,000 until new connections failed.

IPv4 at limit IPv6 at limit Total
Standard stack 45,819 42,000 ~88,000, shared
hcnet 119,992 120,000 240,000, per family

Firewall connection tracking capacity: standard stack about 88,000 shared between IPv4 and IPv6, hcnet 120,000 per address family

With IPv4 traffic only, the standard stack stopped at 83,998 connections and hcnet at 119,994.

On the standard stack, IPv4 and IPv6 hit the wall at the same moment. They draw from one pool of 84,000 to 88,000 entries, slightly above the documented 80,000. On hcnet each family stops at exactly 120,000, independently of the other.

When the table was full on the standard stack, every new connection failed, and the server whose table was full could no longer be reached over SSH. Closing connections does not free the table right away either. After we closed all test connections, the standard stack needed 44 to 64 seconds before new connections succeeded again. hcnet recovered in 0 to 3 seconds.

The fallback behavior shows up clearly with a permissive firewall. With an allow-all Cloud Firewall, the standard stack still stopped at 86,000 connections, because it drops new connections when the table is full. hcnet accepted 160,000, our test maximum, without a single failure: once the table was full, it evaluated packets against the allow rules and let them through.

For a Kubernetes node behind a Cloud Firewall, 88,000 connections is not a lot. An ingress controller, a busy database proxy, or a service mesh sidecar fleet can reach it. On hcnet the ceiling is 1.4 times higher for IPv4 alone, and up to 2.7 times higher when traffic is split between IPv4 and IPv6.

Hetzner also documents a behavior change for the full-table case: hcnet stops tracking and evaluates packets against the firewall rules alone. Traffic that your rules allow explicitly keeps flowing. Only traffic that depends on tracking, such as response packets to outbound connections, is affected.

Packets reach the guest unmerged

The biggest difference we observed between the two stacks is how packets reach the guest.

On the standard stack, the host coalesces incoming TCP segments before handing them to the VM. We measured an average of 12 KB to 38 KB per received packet on the virtual NIC. On hcnet, every packet arrives as it was on the wire: exactly 1,514 bytes on the public interface and 1,464 bytes on the private network. Hetzner documents this for hcnet.

That means 8 to 20 times more packets per gigabit for the guest kernel to process. The guest can still coalesce them with GRO, but the work now happens inside your VM instead of on the host.

Throughput

A single TCP stream runs at 8 to 9 Gbit/s on both stacks. With eight parallel streams into one VM, the standard stack reaches 16 to 18 Gbit/s and hcnet reaches 9 to 10 Gbit/s, on both host pairs.

TCP, 8 streams (Gbit/s) Pair A standard Pair A hcnet Pair B standard Pair B hcnet
Public IPv4 17.2 9.57 18.1 9.87
Public IPv6 17.9 9.15 18.1 9.07
Private network 17.7 9.57 17.5 10.3

Why 8-stream TCP is slower on hcnet: packets arrive at 1,514 bytes instead of 28,196, the receiving vCPU reaches 94% softirq instead of 59%, and throughput drops from 17.2 to 9.87 Gbit/s

The limit is receive-side CPU, not bandwidth. Our CCX23 servers expose a virtio NIC with a single receive queue, and so did the 8-vCPU CCX33 and CPX42 servers we checked, so one vCPU handles all incoming packets. During the eight-stream test on hcnet, that vCPU spent 93% to 97% of its time in software interrupt processing, while the sender’s busiest vCPU stayed more than 60% idle.

To separate the stack from the hosts, we also sent traffic across pairs in every direction. Every hcnet receiver, on all four hosts, stayed at 8.5 to 11.6 Gbit/s, whichever stack the sender used, while every standard-stack receiver reached 15 to 18.6 Gbit/s, including from hcnet senders.

We tried two guest-side mitigations:

Shared vCPU servers are hit harder

Hetzner’s newer shared CPX servers run faster AMD EPYC Genoa cores, but they also have a single receive queue. On the same two CPX42 servers, eight-stream TCP dropped from 7.5-8.9 Gbit/s on the standard stack to 3.5-4.1 Gbit/s on hcnet, and a single stream from about 6.1 to 5.4 Gbit/s. One stream alone pushed the receiving vCPU to 89% softirq on hcnet.

On dedicated CCX servers, about 10 Gbit/s of aggregate TCP ingress per VM is enough for most workloads, but not for bulk data transfer into a single node, such as storage replication. Multi-queue virtio NICs, or host-side coalescing as the standard stack does, would address it.

WireGuard® tunnels

Every node in a Cloudfleet cluster talks to the others over WireGuard®, so we measured tunnel performance separately.

We connected the same servers with a userspace WireGuard® implementation (wireguard-go) and pinned the tunnel to one underlay at a time. All tunnels ran peer to peer, without relays.

WireGuard® tunnel, TCP (Gbit/s) Pair A standard Pair A hcnet Pair B standard Pair B hcnet
4 streams, public IPv4 3.36 4.08 3.95 4.65
4 streams, private network 3.06 4.24 3.80 4.48
4 streams reverse, public IPv4 3.55 4.92 3.33 4.32
1 stream, public IPv4 3.21 4.39 3.93 4.36

Tunnel throughput rose by 18% to 39% with four streams on both host pairs, and by 6% to 37% with a single stream. Tunnel latency did not change. Encryption keeps the tunnel well below the 10 Gbit/s receive ceiling, so the single-queue limit does not apply.

Latency and connection setup

Median latency is about the same on both stacks: 115 to 135 µs for a one-byte TCP request/response over public IPv4, and 0.13 to 0.15 ms ping round trip. hcnet was 6% to 7% faster at the median, with a larger gain at p99:

Public IPv4 Pair A standard Pair A hcnet Pair B standard Pair B hcnet
TCP_RR p99 217 µs 172 µs 189 µs 165 µs
TCP_CRR p99 (new connection) 565 µs 474 µs 508 µs 454 µs

The rate of new connections per second did not change consistently: it rose by 23% on one host pair and stayed flat on the other.

Private networks

On the private network, hcnet was equal or slightly faster: median TCP_RR went from 75 to 68 µs on one pair and from 137 to 126 µs on the other. The host pair mattered far more than the stack, so a workload that is sensitive to private network latency gains more from placement than from switching stacks.

Functional changes we observed

We also confirmed these behavior changes from the hcnet documentation:

Methodology notes and limitations

Should you switch?

Switch to hcnet if your servers:

Test first if a single server needs high aggregate TCP ingress: more than about 10 Gbit/s on dedicated CCX servers, or more than about 4 Gbit/s on shared CPX servers.

The switch is per server, takes effect immediately, and can be reverted in the Hetzner Console. It briefly interrupts connectivity and can drop existing connections when a firewall is attached, so do it during a maintenance window. Plan a reboot or a DHCP renewal afterwards so the new metadata routes appear.

What we want to see next

hcnet is the biggest change to Hetzner Cloud networking in years, and we think the direction is right. Moving firewalls and packet handling into eBPF gives Hetzner a programmable data path, and Hetzner says the stack will pave the way for future network functions. The connection tracking limits alone remove a ceiling that busy Kubernetes nodes run into today.

The single-queue receive path is now the main constraint for high-throughput servers. Multi-queue virtio NICs would remove it, and we hope they are on Hetzner’s roadmap.

We will keep testing hcnet as it moves out of beta, across more locations and instance types, and we will publish what we find. If you run Kubernetes on Hetzner and would rather not track this yourself, talk to us.

See Cloudfleet in action

Book a call with our team for a guided tour of the platform. We will walk you through managed Kubernetes across cloud, datacenter, and edge, and answer your questions.

Request a demo