This lab builds a production-grade network stack: two physical interfaces bonded with LACP, a VLAN subinterface on top of the bond, and end-to-end verification.
Objective
By the end of this lab, you can:
- Build a bond with two physical interfaces in LACP mode.
- Add a VLAN subinterface on the bond.
- Verify LACP negotiation with
/proc/net/bonding/bond0. - Test redundancy by simulating one link’s failure.
- Persist the configuration with Netplan or systemd-networkd.
Architecture
The host has two physical interfaces (eth0 and eth1)
connected to two switch ports configured as a single LACP
channel. The host presents a single bond interface bond0.
A VLAN subinterface bond0.100 carries traffic for VLAN 100.
The switch ports must be configured for LACP active mode. In this lab, if you do not have a real switch available, you can simulate by:
- Using two VMs with a virtual switch in between.
- Using
nm-connection-editoron a host with two interfaces connected to two physical switch ports. - Using
vethpairs in a network namespace to create a loop.
For read-only exploration, ip commands work without a
switch - the bond stays up even if no aggregation occurs.
Requirements
- A lab host with two spare physical or virtual interfaces
(
eth0,eth1) that carry no production traffic. - Console, serial, or IPMI/BMC access to the host, confirmed working before you start.
- Root or sudo on the host.
- Optionally a switch with the two ports in an LACP channel. The lab works without one; LACP simply never negotiates a partner.
Tasks
Task 1: Read and record the current state
Capture the starting configuration. You need these values to restore the host in Cleanup, and you cannot recover them once the interfaces are torn down.
ip -br link show
ip -br addr show
ethtool eth0 | grep -E 'Speed|Link'
ethtool eth1 | grep -E 'Speed|Link'
# Record the addresses and routes you are about to destroy
ip -o addr show dev eth0 | tee ~/lab-eth0-addr.txt
ip route show | tee ~/lab-routes.txt
ip route get 1.1.1.1 | tee ~/lab-default-path.txt
Confirm eth0 and eth1 are up and at the expected speed, and that
~/lab-default-path.txt does not name eth0 or eth1 as the
interface your management traffic uses. If it does, stop and move
to the console.
Task 2: Create the bond
sudo modprobe bonding
sudo ip link add bond0 type bond mode 802.3ad miimon 100 lacp_rate fast
sudo ip link set bond0 up
Verify the bond exists, and verify link monitoring is actually on:
ip -d link show bond0
cat /proc/net/bonding/bond0 # shows bond mode but no slaves yet
cat /sys/class/net/bond0/bonding/miimon # must not be 0
grep 'MII Polling Interval' /proc/net/bonding/bond0
Task 3: Attach the slaves
This is the step that cuts network access. Run it from the console, or from a session on a third interface.
$ sudo ip link set eth0 down
sudo ip link set eth1 down
sudo ip link set eth0 master bond0
sudo ip link set eth1 master bond0
sudo ip link set eth0 up
sudo ip link set eth1 upVerify:
cat /proc/net/bonding/bond0
The output should list both slaves with MII Status: up and
matching Aggregator ID. If the switch is configured
correctly, you will also see partner MAC and key.
Task 4: Test redundancy
There are two failure tests here, and they are not
interchangeable. Run both. The first one always passes. Only the
second one validates miimon.
Task 4a: Administrative down - always detected
$ sudo ip link set eth0 down
cat /proc/net/bonding/bond0
ping -c 3 10.0.0.1 # should still work via eth1This proves the bond is aggregated and traffic survives on one member. It proves nothing about link monitoring.
Task 4b: Real carrier loss - the test that validates miimon
Cause a genuine carrier loss on eth0 by one of these, in order
of preference:
- Unplug the cable, or remove the transceiver.
- Ask the network team to
shutdownthe switch port. - On a VM, detach the virtual NIC from its network from the hypervisor, not from inside the guest.
Then read the bond again:
cat /proc/net/bonding/bond0 # eth0 must show MII Status: down
ping -c 3 10.0.0.1 # must still work via eth1
Then restore the cable or re-enable the port, and restore the administrative state:
sudo ip link set eth0 up
sleep 3
cat /proc/net/bonding/bond0
Confirm both slaves are back to MII Status: up before moving
on. Do not start Task 5 with a degraded bond.
Task 5: Add a VLAN subinterface
sudo ip link add link bond0 name bond0.100 type vlan id 100
sudo ip link set bond0.100 up
sudo ip addr add 10.100.0.10/24 dev bond0.100
Adding the address already installs the connected route for
10.100.0.0/24, so the VLAN gateway is reachable without
touching the default route. If your VLAN subnet is not the one
the address implies, add an explicit route instead:
sudo ip route add 10.100.0.0/24 dev bond0.100
Verify:
ip -d link show bond0.100
ip route show dev bond0.100
ping -c 3 10.100.0.1
The VLAN tag is 100; the host can reach the VLAN gateway.
Task 6: Test VLAN redundancy
$ sudo ip link set eth0 down
ping -c 3 10.100.0.1 # should still work via eth1
sudo ip link set eth0 upThe VLAN rides on the bond; bond redundancy propagates to the VLAN.
Task 7: Persist with Netplan
# /etc/netplan/99-bond-vlan.yaml
network:
version: 2
renderer: networkd
ethernets:
eth0:
dhcp4: no
eth1:
dhcp4: no
bonds:
bond0:
interfaces:
- eth0
- eth1
parameters:
mode: 802.3ad
lacp-rate: fast
transmit-hash-policy: layer3+4
mii-monitor-interval: 100
vlans:
bond0.100:
id: 100
link: bond0
addresses:
- 10.100.0.10/24
# Add a default route here ONLY if this VLAN really is the
# host's default egress path. If the host reaches the world
# over a separate management interface, leave it out - a
# second default route either conflicts or hijacks egress.
# routes:
# - to: default
# via: 10.100.0.1
Apply safely. netplan try applies the configuration and
automatically rolls it back after 120 seconds unless you confirm,
which is exactly the protection you want when a network change
can cut your session:
sudo netplan try
Then commit:
sudo netplan apply
Verify:
cat /proc/net/bonding/bond0
ip -d link show bond0.100
ip route show
ping 10.100.0.1
Task 8: Document the rollback
The rollback is to delete the new file and re-apply:
sudo rm /etc/netplan/99-bond-vlan.yaml
sudo netplan apply
This restores any earlier configuration (often DHCP on eth0).
Validation
- The bond has two slaves with matching
Aggregator ID. cat /sys/class/net/bond0/bonding/miimonprints a non-zero value, and/proc/net/bonding/bond0reports a matchingMII Polling Interval.- LACP partner information is populated (when connected to a switch).
- Pulling one cable - not
ip link set eth0 down- does not break connectivity, and/proc/net/bonding/bond0shows that member asMII Status: down. - The VLAN subinterface is reachable and rides the bond’s redundancy.
- The configuration survives a reboot (after
netplan apply).
Cleanup
Run cleanup from the console too. Tearing the bond down removes the host’s addressing along with it.
Open the captures you took in Task 1 (~/lab-eth0-addr.txt and
~/lab-routes.txt) before you start. You are about to need the
original address, prefix length and default gateway, and after
the teardown there is nowhere left to read them from.
$ # 1. Remove the VLAN and the bond
sudo ip link set bond0.100 down
sudo ip link del bond0.100
sudo ip link set eth0 nomaster
sudo ip link set eth1 nomaster
sudo ip link set bond0 down
sudo ip link del bond0
# 2. Remove the persisted config, if you created it in Task 7
sudo rm -f /etc/netplan/99-bond-vlan.yamlNow restore addressing. Substitute the values you recorded in Task 1; the ones below are only an example:
sudo ip link set eth0 up
sudo ip link set eth1 up
# Static example - use YOUR recorded address and prefix
sudo ip addr add 192.0.2.25/24 dev eth0
sudo ip route add default via 192.0.2.1 dev eth0
If the host was on DHCP before the lab, the cleaner restore is to let the renderer rebuild the configuration rather than typing addresses back by hand:
sudo netplan apply # rebuilds from /etc/netplan/*.yaml
# or, on a networkd host without netplan:
sudo systemctl restart systemd-networkd
Verify the host is genuinely back before you close the console:
# Substitute your own values before running:
GATEWAY=192.0.2.1
ip -br addr show
ip route show
ip route get 1.1.1.1 # compare with ~/lab-default-path.txt
ping -c 3 "$GATEWAY"
The lab is only cleaned up when ip route show and
ip -br addr show match what you recorded in Task 1. Removing
what you built is not the same as restoring what was there.
What you learned
- LACP requires switch configuration. Without it, the bond stays up but no aggregation occurs.
- A VLAN on a bond inherits the bond’s redundancy.
/proc/net/bonding/bond0is the canonical diagnostic.netplan tryis the safe way to apply remote network changes.- Enslaving an interface requires taking it down, so any bond build cuts the session running over that interface. Out-of-band access is a prerequisite for the work, not a fallback.
- Record addresses and routes before you destroy them. A cleanup that removes what you built but does not restore what was there leaves the host unreachable.
- A second default route either conflicts or hijacks egress. Adding one is a separate, deliberate change with its own rollback.