Skip to content

Linux Server Networking with Netplan

A practical guide to configuring multi-port networking on Ubuntu servers using Netplan. Covers static IPs, DHCP, multi-homing (dual ISP + LAN), bonding, VLANs, interface migration, and troubleshooting.

Netplan is a YAML-based network configuration abstraction layer for Ubuntu (18.04+). It doesn’t configure networking directly — it generates configuration for a backend renderer:

  • systemd-networkd (default on Ubuntu Server) — lightweight, no GUI dependencies, ideal for servers
  • NetworkManager (default on Ubuntu Desktop) — full-featured, handles WiFi/VPN/GUI integration

You can set the renderer explicitly:

network:
version: 2
renderer: networkd # or NetworkManager

Config files live in /etc/netplan/ as YAML, processed in lexicographic order (so 01-netcfg.yaml is applied before 99-override.yaml). Multiple files can coexist — later files override earlier ones for the same interface.

Terminal window
# View current config
cat /etc/netplan/01-netcfg.yaml
# List all config files (processed in order)
ls -la /etc/netplan/
# Apply changes
sudo netplan apply
# Test changes (auto-reverts after timeout)
sudo netplan try --timeout 120
# Show current network state (netplan 0.106+, Ubuntu 23.04+)
netplan status
Terminal window
# List all interfaces with link status
for iface in $(ls /sys/class/net/ | grep -v lo); do
link=$(ethtool $iface 2>/dev/null | grep "Link detected:" | awk '{print $3}')
speed=$(ethtool $iface 2>/dev/null | grep "Speed:" | awk '{print $2}')
driver=$(ethtool -i $iface 2>/dev/null | grep driver | awk '{print $2}')
mac=$(ip link show $iface | grep ether | awk '{print $2}')
echo "$iface: link=$link speed=$speed driver=$driver mac=$mac"
done
# See NIC hardware
lspci | grep -i ethernet
# Check max supported speed
ethtool <interface> | grep "Supported link modes"

Linux uses systemd’s Predictable Network Interface Names instead of the old eth0/eth1 scheme. The prefix tells you the hardware type and bus location:

PrefixTypeNaming BasisExample
eno*Onboard ethernetFirmware/BIOS indexeno16495np0
enp*PCIe ethernetPCI bus + slotenp3s0
ens*PCIe ethernetPCI hotplug slotens192 (common in VMs)
enx*USB ethernetMAC addressenx9c69d37c9c17
ib*InfiniBandPCI bus + slotibp23s0
wl*WirelessSame scheme as en*wlp2s0
network:
version: 2
ethernets:
eno16495np0:
addresses:
- 192.168.1.100/24
routes:
- to: default
via: 192.168.1.1
nameservers:
addresses:
- 1.1.1.1
- 8.8.8.8
search: []
dhcp4: false
dhcp6: false
network:
version: 2
ethernets:
eno16495np0:
dhcp4: true
optional: true # Don't block boot if no cable

On servers with many ethernet ports, enable all of them so any cable plugged in auto-configures:

network:
version: 2
ethernets:
# Primary — static IP
eno16495np0:
addresses:
- 192.168.1.19/24
routes:
- to: default
via: 192.168.1.1
nameservers:
addresses:
- 1.1.1.1
- 8.8.8.8
dhcp4: false
# All other ports — DHCP, auto-configure when plugged in
eno16505np1:
dhcp4: true
optional: true
eno16695:
dhcp4: true
optional: true
eno16705:
dhcp4: true
optional: true

Important: optional: true prevents the port from blocking boot when no cable is present. Without it, systemd-networkd waits for a DHCP lease and delays startup by minutes.

Netplan sets uncabled ports to DOWN, which prevents link detection. Create a systemd service to keep them administratively UP:

/root/all-nics-up.sh
#!/bin/bash
for iface in eno16495np0 eno16505np1 eno16695 eno16705; do
ip link set "$iface" up 2>/dev/null
done
/etc/systemd/system/all-nics-up.service
[Unit]
Description=Bring all ethernet interfaces UP
After=network.target
[Service]
Type=oneshot
ExecStart=/root/all-nics-up.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Terminal window
sudo systemctl enable --now all-nics-up.service

Running a public ISP connection alongside a private LAN requires policy-based routing to keep traffic on the correct interface.

With two default gateways, the kernel sends all traffic through whichever has the lower metric — even traffic sourced from the other interface’s IP. This breaks connectivity on the second interface.

This uses Linux’s policy routing database (ip rule + ip route table) to route packets based on their source address rather than just the destination. This is documented in detail in the LARTC HOWTO — Routing Policy Database, with a specific multiple uplinks example. Also see Netplan’s own source_routing.yaml example for the YAML-native approach using routing-policy: keys.

Terminal window
# Add a custom routing table
echo "100 isp" >> /etc/iproute2/rt_tables
# Route traffic FROM the ISP IP through the ISP interface
ip rule add from 46.110.152.202 table isp priority 100
ip route add default via 46.110.152.201 dev eno16495np0 table isp
ip route add 46.110.152.200/29 dev eno16495np0 table isp

Cloudflare WARP (and similar VPN software) installs a catch-all routing table that hijacks ALL traffic — including traffic meant for your ISP interface. Symptoms:

  • ip route get 1.1.1.1 from <ISP_IP> shows dev CloudflareWARP instead of your interface
  • Pings from the ISP IP fail with “Operation not permitted”
  • Traffic works fine through the tunnel but not directly

Fix: Disconnect WARP when testing direct ISP connectivity, or configure WARP split tunneling to exclude your ISP subnet:

Terminal window
warp-cli disconnect # Temporary
warp-cli set-mode proxy # Or switch to proxy-only mode

For permanent fixes, configure split tunnel exclusions in the Zero Trust Dashboard (Settings → WARP Client → Device Settings → Split Tunnels) to exclude your ISP subnet.

Example: ISP on eno16495np0, LAN on eno16695

Section titled “Example: ISP on eno16495np0, LAN on eno16695”
network:
version: 2
ethernets:
# Public ISP — static
eno16495np0:
addresses:
- 46.110.152.202/29
routes:
- to: default
via: 46.110.152.201
nameservers:
addresses:
- 206.225.75.225
- 1.1.1.1
dhcp4: false
# Private LAN — static or DHCP
eno16695:
addresses:
- 192.168.1.19/24
routes:
- to: default
via: 192.168.1.1
metric: 200 # Higher metric = lower priority
dhcp4: false

When changing network config remotely, always set up an auto-revert so you don’t get locked out.

Terminal window
# Applies config, reverts after 120s unless you press Enter
sudo netplan try --timeout 120
Terminal window
# Backup current config
cp /etc/netplan/01-netcfg.yaml /etc/netplan/01-netcfg.yaml.backup
# Schedule revert in 5 minutes
echo "cp /etc/netplan/01-netcfg.yaml.backup /etc/netplan/01-netcfg.yaml && netplan apply" | at now + 5 minutes
# Apply new config
sudo netplan apply
# If it works, cancel the revert
atrm $(atq | awk '{print $1}')

Live Interface Migration (Move Cable Without Losing SSH)

Section titled “Live Interface Migration (Move Cable Without Losing SSH)”

When you need to move your LAN cable to a different port while staying connected:

  1. Don’t assign the same IP to multiple interfaces — causes ARP confusion
  2. Use a watchdog script that detects link loss on the primary and migrates:
#!/bin/bash
# Watches for cable migration between ports
PRIMARY="eno16495np0"
SECONDARY_PORTS="eno16505np1 eno16695 eno16705"
LAN_IP="192.168.1.19/24"
GW="192.168.1.1"
while true; do
PRIMARY_LINK=$(ethtool $PRIMARY 2>/dev/null | grep "Link detected: yes")
if [ -z "$PRIMARY_LINK" ]; then
# Primary lost cable — find where it went
for iface in $SECONDARY_PORTS; do
ip link set $iface up 2>/dev/null
link=$(ethtool $iface 2>/dev/null | grep "Link detected: yes")
if [ -n "$link" ]; then
# Remove from dead primary
ip addr del $LAN_IP dev $PRIMARY 2>/dev/null
ip route del default via $GW dev $PRIMARY 2>/dev/null
# Assign to new port
ip addr add $LAN_IP dev $iface 2>/dev/null
ip route replace default via $GW dev $iface 2>/dev/null
break
fi
done
fi
sleep 3
done

Bonding combines multiple NICs into a single logical interface for redundancy or throughput. This is the natural next step when you have a multi-port server. Netplan supports all Linux bonding modes.

The simplest and most common mode. Only one NIC is active; the other takes over if the primary fails. Works with any switch — no special configuration required.

network:
version: 2
ethernets:
eno16495np0: {}
eno16505np1: {}
bonds:
bond0:
interfaces:
- eno16495np0
- eno16505np1
parameters:
mode: active-backup
primary: eno16495np0
mii-monitor-interval: 100 # Check link every 100ms
addresses:
- 192.168.1.19/24
routes:
- to: default
via: 192.168.1.1
nameservers:
addresses:
- 1.1.1.1
- 8.8.8.8

802.3ad / LACP (Mode 4) — Aggregated Throughput

Section titled “802.3ad / LACP (Mode 4) — Aggregated Throughput”

Requires a managed switch with LACP (Link Aggregation Control Protocol) enabled. Provides both redundancy and increased bandwidth for multi-stream workloads.

network:
version: 2
bonds:
bond0:
interfaces:
- eno16495np0
- eno16505np1
parameters:
mode: 802.3ad
lacp-rate: fast
mii-monitor-interval: 100
transmit-hash-policy: layer3+4 # Hash on IP+port for better distribution
addresses:
- 192.168.1.19/24
routes:
- to: default
via: 192.168.1.1
ModeNameSwitch SupportUse Case
0balance-rrNeeds EtherChannelTesting only (causes packet reordering)
1active-backupAny switchSimple failover
2balance-xorNeeds EtherChannelStatic load distribution
3broadcastNeeds EtherChannelFault tolerance (sends on all)
4802.3adNeeds LACPProduction aggregation
5balance-tlbAny switchOutbound load balancing
6balance-albAny switchBi-directional load balancing

VLANs let you segment traffic on a single physical NIC into isolated virtual networks. Common uses: separating management traffic from data, isolating IoT devices, or running multiple subnets on one NIC.

network:
version: 2
ethernets:
eno16495np0:
dhcp4: false
vlans:
vlan100:
id: 100
link: eno16495np0
addresses:
- 10.100.0.10/24
routes:
- to: 10.100.0.0/24
via: 10.100.0.1
vlan200:
id: 200
link: eno16495np0
addresses:
- 10.200.0.10/24

You can stack VLANs on top of a bond for both redundancy and segmentation:

network:
version: 2
bonds:
bond0:
interfaces: [eno16495np0, eno16505np1]
parameters:
mode: 802.3ad
vlans:
vlan100:
id: 100
link: bond0
addresses:
- 10.100.0.10/24

ethtool is the primary tool for querying and configuring NIC hardware settings:

Terminal window
ethtool eno16495np0 | grep -E "Speed|Duplex|Auto-negotiation|Supported link modes"
SymptomCauseFix
1 Gbps on a 10G NICOther end only offers 1GCheck switch/ISP port, cable category
Half duplexBad cable or USB adapterReplace cable, avoid USB hubs
Link flappingCable too long or damagedMax 100m for Cat6a at 10G
”Speed: Unknown”No cable detectedCheck physical connection
SpeedMin CableMax Distance
1 GbECat5e100m
2.5 GbECat5e100m
5 GbECat6100m
10 GbECat6a100m
25/40 GbEDAC/FiberVaries

Servers with Mellanox ConnectX or Broadcom BCM57414 NICs have SFP+/QSFP ports for 10G+ speeds. These require:

  • SFP+ transceiver (10GBase-T for copper, 10GBase-SR for fiber)
  • DAC cable (Direct Attach Copper — cheap, short range, no transceiver needed)
  • Check compatibility: ethtool -m <interface> shows transceiver info

USB ethernet adapters work but have caveats:

  • Often limited to 1 Gbps (USB 3.0 bandwidth)
  • May use generic cdc_ncm driver instead of vendor driver — can cause half-duplex, no traffic
  • USB hubs add latency and may not pass ethernet reliably
  • Interface names are MAC-based: enx9c69d37c9c17

For servers: always prefer built-in NIC ports over USB adapters.

When adding a second network interface, UFW may block traffic on the new interface.

Terminal window
# Allow SSH on the new interface
sudo ufw allow in on eno16495np0 to any port 22 proto tcp
# Allow all outbound (default)
sudo ufw default allow outgoing
# Check status
sudo ufw status verbose
Terminal window
# Check ARP — can you resolve the gateway MAC?
ip neigh show dev <interface>
# Check routing — is traffic going to the right interface?
ip route get <gateway> from <your_ip>
# Check firewall
sudo iptables -L -n -v | head -20
sudo ufw status
# Check for VPN/tunnel hijacking routes
ip rule list
ip route show table all | grep -v "local\|broadcast"
  1. Wait for auto-revert (if you set one up)
  2. Try alternate access: Cloudflare tunnel, IPMI/iDRAC console, physical console
  3. If physically on-site: sudo cp /etc/netplan/*.backup /etc/netplan/01-netcfg.yaml && sudo netplan apply

Never assign the same IP to two active interfaces on the same subnet. The kernel sends ARP replies from both MACs, confusing the switch/router. Traffic randomly goes to the wrong port and connections drop.

When running a server with both public (ISP) and private (LAN) interfaces, isolate them:

Terminal window
# Only allow SSH from the LAN, not the ISP-facing interface
sudo ufw allow in on eno16695 to any port 22 proto tcp
sudo ufw deny in on eno16495np0 to any port 22 proto tcp
# Allow specific services on the public interface
sudo ufw allow in on eno16495np0 to any port 443 proto tcp
sudo ufw allow in on eno16495np0 to any port 80 proto tcp

Add to /etc/sysctl.d/99-network-hardening.conf:

# Reverse path filtering — drop packets arriving on wrong interface
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Don't act as a router (unless you intend to)
net.ipv4.ip_forward = 0
# Ignore ICMP redirects (prevents route injection attacks)
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
# Log packets with impossible source addresses
net.ipv4.conf.all.log_martians = 1
Terminal window
sudo sysctl --system # Reload
Terminal window
# Apply config
sudo netplan apply
# Safe apply with auto-revert
sudo netplan try --timeout 120
# Show all IPs
ip -br addr
# Show routes
ip route
# Show link status
ip -br link
# Show policy routing rules
ip rule list
# Flush interface config
ip addr flush dev <interface>
# Bring interface up/down
ip link set <interface> up
ip link set <interface> down
# Check NIC speed capability
ethtool <interface> | grep "Supported link modes"
# Check cable/link
ethtool <interface> | grep "Link detected"
# Show network state (netplan 0.106+)
netplan status
# Show systemd-networkd managed interfaces
networkctl status
# Debug netplan config generation
netplan generate # Writes backend config, shows errors
  • ethtool(8) man page — Reference for querying NIC speed, driver info, link status, offload features, and transceiver details (v6.15)
  • systemd.net-naming-scheme(7) — Versioned naming scheme details (v238–v260) explaining how each kernel/systemd version assigns interface names
  • ServeTheHome Forums — Community discussions on server NIC compatibility, SFP+ transceivers, and homelab networking
  • r/homelab — Community for self-hosted infrastructure discussions