Hetzner networking
Hetzner needs host-side network configuration before instances can use extra IPs or subnets. Use Bridge when you buy additional single IPs. Use Routed when you buy a subnet and choose your server’s main IP as its gateway.
Hetzner-specific facts
Section titled “Hetzner-specific facts”These apply to every example below. The values come from Hetzner’s official network documentation.
- Bridge name:
br0. Do not name your bridgevirbr0; libvirt reserves that name for its default NAT network and the two collide. - Bridge MAC must match the physical NIC’s MAC. Hetzner switch ports have strict IP/MAC binding. A bridge with a random MAC is silently filtered by the upstream switch. On Debian, the
bridge_hwdirective does this and has been required since Debian 11;hwaddress ether <MAC>is an equivalent alternative. - IPv4: the additional or main IP is configured with a
/32netmask plus an on-link route to the gateway. Hetzner’s gateway sits outside your/32, so the on-link flag tells Linux the gateway is still directly reachable. - IPv6 gateway:
fe80::1(link-local). - IPv6 routing: Hetzner routes the whole
/64to a single MAC. Either leave it on the host’s own MAC, or route the subnet to the virtual MAC of one additional IP in Robot; instances then take addresses from that/64with gatewayfe80::1. - Hetzner DNS:
185.12.64.1,185.12.64.2(IPv4) and2a01:4ff:ff00::add:1,2a01:4ff:ff00::add:2(IPv6). - Interface names in the examples (
enp0s31f6) are examples. Find your real one withip route | grep defaultand substitute it everywhere. - Virtual MACs. When you assign an additional single IP to an instance, generate a virtual MAC for that IP in the Robot console and assign it to the instance NIC. Without it Hetzner filters guest traffic.
Replace the addresses, gateway and MAC below with your own. The examples use documentation values (203.0.113.236, gateway 203.0.113.193, 2001:db8:10:1526::2, MAC 02:00:00:12:34:56).
Edit /etc/network/interfaces:
### Hetzner Online GmbH installimagesource /etc/network/interfaces.d/*
auto loiface lo inet loopbackiface lo inet6 loopback
# Physical interface - no IP, no auto-bring-up of its ownauto enp0s31f6iface enp0s31f6 inet manual
# Bridge - carries the physical NIC's MACauto br0iface br0 inet staticaddress 203.0.113.236netmask 255.255.255.255gateway 203.0.113.193pointopoint 203.0.113.193bridge_hw enp0s31f6bridge_ports enp0s31f6bridge_stp offbridge_fd 1bridge_hello 2bridge_maxage 12dns-nameservers 185.12.64.1 185.12.64.2
iface br0 inet6 staticaddress 2001:db8:10:1526::2/64gateway fe80::1bridge_hw sets the bridge’s MAC to the physical NIC’s MAC; it is the directive Hetzner’s own documentation uses. hwaddress ether <MAC> does the same job and also works. The pointopoint directive does the same job as netplan’s on-link: true: it tells Debian the gateway is directly reachable even though it sits outside the /32.
Read the MAC address of the physical NIC:
nmcli connection show "enp0s31f6" | grep mac-addressHetzner’s own documentation configures RHEL with ifcfg files. Create /etc/sysconfig/network-scripts/ifcfg-enp0s31f6:
DEVICE=enp0s31f6BRIDGE=br0HWADDR=02:00:00:12:34:56ONBOOT=yesThen /etc/sysconfig/network-scripts/ifcfg-br0:
DEVICE=br0TYPE=BridgeBOOTPROTO=staticIPADDR=203.0.113.236NETMASK=255.255.255.255GATEWAY=203.0.113.193SCOPE="peer 203.0.113.193"DNS1=185.12.64.1DNS2=185.12.64.2DELAY=0ONBOOT=yesSCOPE="peer 203.0.113.193" is the ifcfg equivalent of Debian’s pointopoint: it tells the network scripts the gateway is directly reachable even though it sits outside the /32. Restart networking to apply, then add IPv6 with nmcli (the ifcfg files above cover IPv4 only):
nmcli connection modify br0 ipv6.addresses '2001:db8:10:1526::2/64' ipv6.gateway 'fe80::1' ipv6.dns '2a01:4ff:ff00::add:1 2a01:4ff:ff00::add:2' ipv6.method manualnmcli connection up br0Alternatively, configure the whole bridge with nmcli instead of ifcfg files:
nmcli connection add type bridge con-name br0 ifname br0nmcli connection modify br0 ipv4.addresses '203.0.113.236/32' ipv4.gateway '203.0.113.193' ipv4.dns '185.12.64.1 185.12.64.2' ipv4.method manualnmcli connection modify br0 ipv6.addresses '2001:db8:10:1526::2/64' ipv6.gateway 'fe80::1' ipv6.dns '2a01:4ff:ff00::add:1 2a01:4ff:ff00::add:2' ipv6.method manualnmcli connection modify enp0s31f6 master br0nmcli connection modify br0 connection.autoconnect-slaves 1nmcli connection modify br0 ethernet.cloned-mac-address 02:00:00:12:34:56nmcli connection up br0 && nmcli connection up enp0s31f6Replace the contents of /etc/netplan/01-netcfg.yaml:
network: version: 2 renderer: networkd ethernets: enp0s31f6: match: macaddress: 02:00:00:12:34:56 dhcp4: no dhcp6: no bridges: br0: macaddress: 02:00:00:12:34:56 interfaces: - enp0s31f6 dhcp4: no dhcp6: no addresses: - 203.0.113.236/32 - 2001:db8:10:1526::2/64 routes: - to: 0.0.0.0/0 via: 203.0.113.193 on-link: true - to: "::/0" via: "fe80::1" on-link: true nameservers: addresses: - 185.12.64.1 - 2a01:4ff:ff00::add:2 - 185.12.64.2 - 2a01:4ff:ff00::add:1Apply with:
netplan applyReboot the server to confirm the configuration survives a restart.
Guest configuration
Section titled “Guest configuration”Instances also need static configuration; they do not use DHCP. The instance NIC must use the virtual MAC generated in Robot for that IP, and its gateway is the gateway of the host’s main IP, not a gateway of its own. The example below uses the documentation value 203.0.113.50 for the additional IP.
network: version: 2 ethernets: eth0: dhcp4: no addresses: - 203.0.113.50/32 routes: - to: default via: 203.0.113.193 on-link: true nameservers: addresses: [185.12.64.1, 185.12.64.2]auto eth0iface eth0 inet staticaddress 203.0.113.50netmask 255.255.255.255gateway 203.0.113.193pointopoint 203.0.113.193VirtConsole applies this automatically through cloud-init when you set up the subnet as Bridge mode: enter the main IP’s gateway as the subnet gateway and 255.255.255.255 as the netmask, then attach the additional IP with its virtual MAC to the instance.
Use routed mode when you buy a subnet from Hetzner. When ordering, choose the primary IP of your server as the subnet’s gateway. Hetzner subnets are always statically routed to your main IP, never bridged, and no gateway address is assigned inside the subnet by Hetzner; the host itself terminates the routing.
-
Enable IP forwarding on the hypervisor and persist it across reboots:
/etc/sysctl.d/99-routed-networking.conf net.ipv4.ip_forward = 1net.ipv6.conf.all.forwarding = 1Terminal window sysctl -p /etc/sysctl.d/99-routed-networking.conf -
Create a libvirt network definition on the hypervisor:
Terminal window mkdir -p /opt/virtconsole/storage/networks/cd /opt/virtconsole/storage/networks/nano <BRIDGE>.xmlvirbr1.xml <network><name>BRIDGE</name><forward mode='route' dev='INTERFACE'/><bridge name='BRIDGE' stp='on' delay='0'/><ip address='GATEWAY' netmask='NETMASK'/></network>BRIDGE: the bridge name, for examplevirbr1.INTERFACE: your primary interface (ip route | grep default).GATEWAY: the first IP of the subnet you purchased, used as the gateway for instances.NETMASK: the netmask of the subnet you purchased, not a fixed value. A/29subnet is255.255.255.248, a/28is255.255.255.240, and so on.
-
Define and start the network:
Terminal window virsh net-define --file <BRIDGE>.xmlvirsh net-autostart <BRIDGE>virsh net-start <BRIDGE>
Because the host itself terminates the routing rather than relying on classic subnet broadcast semantics, every IP of the subnet is usable for instances, including the addresses that would normally serve as the network and broadcast address.
Guest configuration
Section titled “Guest configuration”Instances take a static IP from the subnet with the subnet’s own netmask, and use the host’s bridge IP as their gateway. The example below uses the documentation subnet 203.0.113.128/29, with the host’s bridge on 203.0.113.128 (the subnet’s first IP, usable here because the host terminates the routing) and an instance on 203.0.113.130.
network: version: 2 ethernets: eth0: dhcp4: no addresses: - 203.0.113.130/29 routes: - to: default via: 203.0.113.128 nameservers: addresses: [185.12.64.1, 185.12.64.2]auto eth0iface eth0 inet staticaddress 203.0.113.130netmask 255.255.255.248gateway 203.0.113.128In VirtConsole, configure the subnet with the host’s bridge IP (203.0.113.128 in this example) as the gateway and the subnet’s real netmask, not 255.255.255.0.
Changing a routed bridge later
Section titled “Changing a routed bridge later”Edits only take effect after a full restart of the bridge.
cd /opt/virtconsole/storage/networksvirsh net-destroy <BRIDGE>virsh net-define --file <BRIDGE>.xmlvirsh net-start <BRIDGE>Then relink the instance interfaces:
virsh list --name | xargs -i virsh domiflist {} | grep -w <BRIDGE> | awk '{print $1}' | xargs -i ip link set {} master <BRIDGE>Common problems
Section titled “Common problems”- Instances with extra IPs have no connectivity. The virtual MAC is missing. Generate one in Robot for the IP and assign it to the instance NIC.
- The host is unreachable after
netplan apply. The bridge MAC does not match the physical NIC, or the gateway route is missingon-link: true.

