How to Configure GENEVE Tunnels in Ubuntu Using systemd-networkd

Generic Network Virtualisation Encapsulation (GENEVE) is a network virtualisation technology that unifies the capabilities of VXLAN, NVGRE, and Geneve by providing an extensible, metadata-rich encapsulation header over standard UDP transport. In enterprise data centres, multi-tenant cloud environments, and containerised orchestrators such as Kubernetes using Open Virtual Network (OVN), GENEVE serves as the default overlay tunneling mechanism. Ubuntu Server provides native, kernel-level GENEVE tunnel configuration through systemd-networkd, avoiding manual shell scripts or fragile runtime commands.

Understanding GENEVE Tunnel Architecture in systemd-networkd

GENEVE wraps Layer 2 Ethernet frames inside standard IPv4 or IPv6 UDP datagrams, traditionally transmitted over destination port 6081. Unlike VXLAN, which features a rigid, static header format with a 24-bit Virtual Network Identifier (VNI), GENEVE introduces Variable Length Options (TLV — Type, Length, Value format). This enables control planes like Open vSwitch (OVS) and OVN to insert dynamic tenant metadata, flow tracing identifiers, and security group tags directly into the packet header.

When provisioning GENEVE tunnels using systemd-networkd, the network daemon orchestrates two distinct configuration structures within /etc/systemd/network/:

  • Virtual Device Definition (.netdev): Declares the virtual GENEVE interface, its remote endpoint IP, its local binding IP, UDP destination port, and tunnel identifier (VNI).
  • Network Link Configuration (.network): Binds IP subnets, routing policies, and MTU specifications to the newly instantiated virtual interface.

Configuring the Underlying Host Network

Before instantiating the GENEVE tunnel, verify that the physical or primary network interface is managed by systemd-networkd and has a static IP address reachable by the remote hypervisor or tunnel gateway. For this example, assume the local physical interface is eth0 with an IPv4 address of 192.168.10.15/24, communicating with a remote endpoint at 192.168.10.25.

Inspect the local interface state using networkctl:

networkctl status eth0

Verify network reachability and ensure UDP port 6081 is open in the local firewall:

sudo ufw allow 6081/udp comment "GENEVE Overlay Tunneling"
sudo ufw status verbose

Creating the GENEVE Virtual Device Definition (.netdev)

Create a dedicated netdev file defining the GENEVE overlay interface. By convention, name the configuration file with a descriptive prefix to ensure proper lexical ordering.

Execute the following command to create /etc/systemd/network/25-geneve0.netdev:

sudo nano /etc/systemd/network/25-geneve0.netdev

Insert the following declarative configuration:

[NetDev]
Name=geneve0
Kind=geneve
Description=GENEVE Overlay Tunnel Interface

[GENEVE]
Id=5001
Remote=192.168.10.25
Local=192.168.10.15
DestinationPort=6081
UDPChecksum=true
TOS=inherit
TTL=64

Key parameters within the [GENEVE] stanza include:

  • Id=5001: Sets the 24-bit Virtual Network Identifier (VNI) corresponding to the tenant or broadcast domain.
  • Remote=192.168.10.25: The underlay IP address of the remote endpoint or peer hypervisor.
  • Local=192.168.10.15: The underlay source IP address bound to the local network interface.
  • DestinationPort=6081: Standard IANA-assigned UDP port for GENEVE traffic.
  • UDPChecksum=true: Enforces outer UDP checksum calculation, essential when routing encapsulated traffic across modern smartNICs or through intermediate transit routers.
  • TOS=inherit: Copies Type of Service / Differentiated Services Code Point (DSCP) flags from inner headers to the outer UDP datagram to preserve quality of service.

Assigning IP Addressing and MTU Parameters (.network)

GENEVE headers add 50 bytes of encapsulation overhead (20 bytes outer IPv4 + 8 bytes outer UDP + 8 bytes base GENEVE header + 14 bytes inner Ethernet frame header), or more if Variable Length TLV options are actively populated. When traversing standard 1500-byte MTU underlay networks, the inner tunnel interface MTU must be reduced to 1450 (or the physical underlay must support jumbo frames of 9000 bytes) to eliminate IP packet fragmentation.

Create the corresponding network definition file /etc/systemd/network/25-geneve0.network:

sudo nano /etc/systemd/network/25-geneve0.network

Populate the file with the overlay subnet parameters:

[Match]
Name=geneve0

[Network]
Description=Overlay Subnet for Tenant Alpha
Address=10.200.1.15/24
LinkLocalAddressing=no
ConfigureWithoutCarrier=yes

[Link]
MTUBytes=1450
RequiredForOnline=no

Setting ConfigureWithoutCarrier=yes ensures systemd-networkd assigns the configured IP address and brings the interface up immediately without waiting for an active physical carrier detection on the virtual device.

Applying Configurations and Validating Tunnel Operation

Reload systemd-networkd to process the new configuration files and instantiate the virtual GENEVE device:

sudo systemctl restart systemd-networkd

Inspect the status of the new interface using networkctl:

networkctl status geneve0

The output will show Type: geneve, Operational: routable, and confirm the assigned overlay address 10.200.1.15/24.

Examine low-level kernel interface properties using the ip command:

ip -d link show geneve0

The command outputs detailed tunnel attributes confirming the VNI (id 5001), remote IP (remote 192.168.10.25), and encapsulation port (dstport 6081):

geneve0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT
    link/ether 32:a4:8d:fe:12:44 brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 68 maxmtu 65535
    geneve id 5001 remote 192.168.10.25 local 192.168.10.15 dstport 6081 ttl 64 tos inherit df set udpcsum numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

Verify overlay connectivity by pinging the remote tunnel peer over the overlay network:

ping -c 4 10.200.1.25

To inspect live encapsulated packets traversing the underlay interface, run tcpdump filtering for UDP port 6081:

sudo tcpdump -ni eth0 udp port 6081 -vv

Troubleshooting Common GENEVE Configuration Pitfalls

If encapsulated packets fail to reach the peer or inner applications experience dropped connections, evaluate the following diagnostic areas:

  • Path MTU Black Holes: If small ICMP echo requests succeed but large TCP file transfers hang, intermediate routers are dropping packets larger than 1500 bytes. Ensure MTUBytes=1450 is configured on both peers, or enable TCP Maximum Segment Size (MSS) clamping via nftables or iptables:
    sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
  • Firewall Ingress Rejection: Standard firewall profiles frequently block UDP port 6081. Confirm ingress rules allow traffic from the remote underlay IP:
    sudo iptables -A INPUT -p udp --dport 6081 -s 192.168.10.25 -j ACCEPT
  • Lexical File Ordering: Ensure netdev and network files use identical naming conventions (e.g. 25-geneve0.netdev and 25-geneve0.network). If a catch-all network configuration file (such as 99-default.network) matches Name=*, it can override link-specific parameters.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.