christoph ender's

blog

tuesday the 2nd of june, 2026

Routing and tunneling at Hetzner

A few weeks ago, I encountered a situation in which I needed to route traffic via multiple VPN nodes into a private virtual network at Hetzner and provide remote systems with access to all nodes within the private network. For redundancy purposes, there were two VPN gateways that would accept traffic from outside. It turned out, however, that Hetzner's routing only accepts traffic for a subnet when it comes through a link that has that subnet configured as a routing destination. As a result, by default, only one node can route the VPN traffic to the Hetzner nodes.

Hetzner's routing inside private networks is designed in such a way that all traffic always has to pass through the network's gateway, which always occupies the first IP address of a subnet. Consequently, even in case two nodes reside in the same subnet 10.1.0.0/24, they will still have to direct their traffic through the gateway at 10.1.0.1 in order to communicate with each other.

Furthermore, multiple routes in Hetzner's private networks cannot share the same destination or point to overlapping destination networks, with the exception of 0.0.0.0/0.

Now, consider a setup having two VPN gateways, both of which are supposed to provide connectivity from the Hetzner private network 10.1.0.0/16 to a remote subnet 10.2.0.0/16. Due to the routing restrictions mentioned above, only one can be configured as the route to the remote subnet. While that limitation might be acceptable, there's another restriction: only one gateway can forward traffic from the remote network into the private one. The network gateway, through which all traffic within the private network must pass, apparently drops traffic arriving through a VPN gateway that is not configured as the route to the remote subnet, presumably due to anti-spoofing measures.

In order to allow traffic from the remote side to travel through the Hetzner network anyway, I've decided to set up a set of tunnels between the nodes. The idea is to have two GRE-Tunnels per host, connecting each host to both VPN gateways. The tunnel encapsulates the packets from the remote network. However, this was only doable since there were only a few hosts on the Hetzner side, else the number of required tunnels would have gotten out of hand.

/etc/network/interfaces
auto gre01
iface gre01 inet static
  address 172.16.0.1
  netmask 255.255.255.0
  pre-up ip tunnel add gre01 mode gre \
    local 10.100.0.10 \
    remote 10.100.0.20 \
    ttl 255 \
    key 101
  up ip link set gre01 up
  up ip route add 172.16.0.2/32 dev gre01
  post-down ip tunnel del gre01