Introduction
In this tutorial, you'll learn how to set up a routed network configuration where the host and a virtual machine (guest) are in two different subnets. At the end, the configuration should look like this:
HOST:
|
GUEST:
|
To achieve this, we will setup one virtual bridge that takes control of the main interface (NOT bridged to enp7s0).
For a bridged setup (host and guest in same subnet), see this tutorial.
Prerequisites
- Server has 1 physical uplink interface -
enp7s0with MAC addressAA:BB:CC:DD:EE:FF - Server has main IPv4 (
enp7s0) - Server has 1x additional single IPv4 (virtual MAC address was disabled via Robot account)
- OR Server has 1x additional IPv4 aubnet - A single IPv4 in a routed configuration is functionally just a
/32subnet. - Server has a
/64IPv6 subnet for the HOST and GUEST - We use regular qemu in console for this example scenario
Example terminology
2001:db8:1234:: # Placeholder for public IPv6 network of the server
10.0.0.168 # Placeholder for main IPv4
10.0.10.135 # Placeholder for additional single IPv4
10.10.10.128/29 # Placeholder for additional IPv4 Network
AA:BB:CC:DD:EE:FF # Placeholder for MAC address of physical interface and main IPv4
00:50:56:00:11:22 # Placeholder for virtual MAC address of additional single IP addressNote: The start command shown in the example is for illustrative purposes only and does not constitute instructions. Unfortunately, we cannot offer support for setting up or operating a virtualization environment.
Step 1 - Configure Netplan on HOST
nano /etc/netplan/01-netcfg.yamlnetwork:
version: 2
renderer: networkd
ethernets:
enp7s0:
addresses:
- 10.0.0.168/32 # Main IPv4 for the HOST
- 2001:db8:1234::2/64 # Main IPv6/64 subnet for the HOST
routes:
- on-link: true
to: 0.0.0.0/0
via: 10.0.0.129 # Gateway IPv4 of the main IPv4 address
- to: default
via: fe80::1 # Default Hetzner IPv6 gateway
nameservers:
addresses:
- 185.12.64.2 # Nameservers from installimage process
- 2a01:4ff:ff00::add:1 # Nameservers from installimage process
- 185.12.64.1 # Nameservers from installimage process
- 2a01:4ff:ff00::add:2 # Nameservers from installimage process
bridges:
vibr0:
interfaces: [] # Bridge not connected to enp7s0
addresses:
- 10.0.0.168/32 # Main IPv4 for the HOST
- 2001:db8:1234::2/64 # Main IPv6/64 subnet for the HOST
routes:
- to: 10.0.10.135/32 # Route for the additional IPv4 (or whole IPv4 subnet)
scope: host # Required, so netplan creates a local route
- to: 2001:db8:1234::3/128 # Route for the IPv6 of the VM
scope: host # Required, so netplan creates a local routeNetplan will by default not create a route for local connections.
scope: host forces netplan to create a route anyways.
This is identical to
/etc/network/interfaces
post-up ip route add 10.0.10.135/32 dev vmbr0
Step 2 - Configure tap interface on HOST
Run the following commands to connect HOST and GUEST.
This is not persistent across reboots.
ip tuntap add dev tap0 mode tap user $(whoami)
ip link set tap0 up
ip link set tap0 master vibr0To make this persistent, you can utilize a service file:
nano /etc/systemd/system/tap0.service[Unit]
Description=Persistent tap0 interface
After=network-pre.target
Before=network.target
Wants=network.target
[Service]
Type=oneshot
ExecStart=/usr/sbin/ip tuntap add dev tap0 mode tap user root
ExecStartPost=/usr/sbin/ip link set tap0 up
ExecStartPost=/usr/sbin/ip link set tap0 master vibr0
ExecStop=/usr/sbin/ip link set tap0 down
ExecStopPost=/usr/sbin/ip tuntap del dev tap0 mode tap
RemainAfterExit=yes
[Install]
WantedBy=multi-user.targetsystemctl daemon-reload
systemctl enable tap0.serviceImportant note: The service example defines the user root. Please adjust the user to match the user, who runs the VM in production later
Step 3 - Enable IP forwarding
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv6.conf.all.forwarding=1To make these changes persistent across reboots, add these 2 lines to /etc/sysctl.d/99-forwarding.conf:
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1If iptables blocks IP forwarding, please allow it:
iptables -A FORWARD -i vibr0 -j ACCEPT
iptables -A FORWARD -o vibr0 -j ACCEPT
ip6tables -A FORWARD -i vibr0 -j ACCEPT
ip6tables -A FORWARD -o vibr0 -j ACCEPTThis is not required for the Hetzner standard image of Ubuntu 24.04
Step 4 - Start VM
qemu-system-x86_64 \
-enable-kvm \
-smp 4 `### 4 CPU cores` \
-m 4096 `### 4GB RAM` \
-cpu host `### Use the native CPU architecture` \
-hda vm0.vpc `### Virtual HDD file for the OS in this case` \
-usbdevice tablet `### Emulate USB tablet for HID input (optional)` \
-k en-us `### Keyboard layout (optional)` \
-vnc 127.0.0.1:1 `### Launch VNC for visualistation (optional) - Please set up an encrypted tunnel to not expose the unencrypted VNC connection` \
-monitor stdio `### Launch qemu in interactive mode - Allow adjustments to the VM on the fly)` \
-netdev tap,id=net0,ifname=tap0,script=no,downscript=no `### Assign a network device to the VM via the tap0 created earlier` \
-device virtio-net-pci,netdev=net0 `### Start VM without MAC - routing prevents random mac from leaking - no abuse`Step 5 - Configure netplan of GUEST
nano /etc/netplan/01-netcfg.yamlnetwork:
version: 2
renderer: networkd
ethernets:
ens3: # Network identifier of the VM according to predictable naming scheme
addresses:
- 10.0.10.135/32 # The single IPv4 Address for the GUEST
- 2001:db8:1234::3/128 # A free IP of the IPv6 Subnet
routes:
- to: default # Default 0.0.0.0/0 route
via: 10.0.0.168 # Main IPv4 is the gateway for the IPv4 (or subnet)
on-link: true # Required for IPv4/32 configuration
- to: default # Default IPv6 route
via: 2001:db8:1234::2 # HOST IPv6 as gateway
on-link: true # Required for IPv4/32 configuration
nameservers:
addresses:
- 185.12.64.1 # Nameservers from installimage process
- 2a01:4ff:ff00::add:2 # Nameservers from installimage process
- 185.12.64.2 # Nameservers from installimage process
- 2a01:4ff:ff00::add:1 # Nameservers from installimage processRouted setups allow you to use your free IPv6 /64 subnet for multiple virtual machines and your host simultaneously without conflicting MAC addresses. You can also use a routed setup to spread IPv4 subnets across multiple machines.
Conclusion
After completing this guide, the host and virtual machine are successfully connected using a routed setup, with both operating in separate subnets. The host keeps its main IPv4 address and uses an IPv6 address from the /64 subnet, while the guest receives its dedicated IPv4 address routed through the host and also an IPv6 address from the same /64 subnet. This configuration provides clean routing between host and guest, full IPv6 support, and clear network separation without MAC address conflicts. It is especially suitable for scenarios where multiple virtual machines share the same IPv6 /64 subnet or use dedicated IPv4 subnets.