Quick Start: STP on Open vSwitch in 5 Minutes
Theory can wait. Here’s the fastest path to getting STP running on your Linux machine with Open vSwitch (OVS). If you haven’t installed OVS yet:
sudo apt-get install openvswitch-switch -y
sudo systemctl start openvswitch-switch
sudo systemctl enable openvswitch-switch
Create a bridge and flip STP on:
# Create an OVS bridge
sudo ovs-vsctl add-br br0
# Enable STP on the bridge
sudo ovs-vsctl set bridge br0 stp_enable=true
# Verify STP is active
sudo ovs-vsctl get bridge br0 stp_enable
Done. STP is now active on br0. Add ports and OVS handles the rest — it elects a root bridge and blocks redundant paths automatically:
# Add ports to the bridge
sudo ovs-vsctl add-port br0 eth1
sudo ovs-vsctl add-port br0 eth2
# Inspect port STP state
sudo ovs-ofctl show br0
Deep Dive: What STP Actually Does and Why You Need It
Running virtual switches in a lab or cloud environment with OVS almost always means redundant paths somewhere. That’s the right call for availability — but without STP, those redundant paths will eventually trigger a broadcast storm. Every switch floods the frame, it bounces back, gets flooded again. On a 1Gbps link, you can saturate the entire segment in under 3 seconds.
STP (IEEE 802.1D) fixes this by blocking some ports. Only one active path exists between any two bridges at a time. When it fails, STP recalculates and opens an alternate — though classic STP takes 30–50 seconds to converge, long enough to drop active sessions and generate support calls.
STP vs RSTP vs MSTP — Picking the Right One
Open vSwitch supports all three:
- STP (802.1D) — classic, slow convergence (~50 seconds), avoid in anything production-facing
- RSTP (802.1w) — rapid convergence (1–2 seconds), backward compatible with STP, the sensible default for most setups
- MSTP (802.1s) — multiple spanning tree instances, maps VLANs to different trees, best for complex multi-VLAN environments where you need real load balancing across redundant uplinks
RSTP is the right call for most OVS deployments. I’ve run it in production on KVM hypervisor clusters with bonded uplinks — zero broadcast storms, and when an uplink dropped, failover completed in under 2 seconds. It just works.
STP Port States Explained
Each STP-managed port moves through these states:
- Blocking — receiving BPDUs but not forwarding traffic
- Listening — participating in root bridge election
- Learning — building the MAC address table, not yet forwarding
- Forwarding — fully active, passing traffic normally
- Disabled — administratively shut down
RSTP can skip straight to forwarding under the right conditions. Edge ports connecting to end devices and point-to-point links between switches go active immediately — no waiting. That’s what cuts convergence from 50 seconds down to under 2.
Root Bridge Election
STP picks a root bridge by comparing Bridge IDs — a combination of priority (default 32768) and MAC address. Lowest wins. Since MAC addresses are outside your control, leaving priority at default means root bridge selection is effectively random. Set it explicitly:
# Lower priority = higher chance of becoming root bridge
# Priority must be a multiple of 4096 (0, 4096, 8192... 61440)
sudo ovs-vsctl set bridge br0 other-config:stp-priority=4096
# Verify
sudo ovs-vsctl get bridge br0 other-config
Advanced Usage: RSTP and MSTP Configuration
Switching to RSTP
OVS treats RSTP as a separate flag from classic STP — you can’t just flip a mode. Enable it explicitly:
# Disable STP first if it was enabled
sudo ovs-vsctl set bridge br0 stp_enable=false
# Enable RSTP
sudo ovs-vsctl set bridge br0 rstp_enable=true
# Confirm
sudo ovs-vsctl get bridge br0 rstp_enable
Tuning Port Path Cost and Priority
Path cost tells STP which route to prefer. Lower cost means the port is preferred for forwarding. Port priority breaks ties when two ports have identical path costs:
# Prefer eth1 over eth2 by giving it a lower path cost
sudo ovs-vsctl set port eth1 other-config:rstp-path-cost=100
sudo ovs-vsctl set port eth2 other-config:rstp-path-cost=200
# Set port priority (lower = preferred, default 128)
sudo ovs-vsctl set port eth1 other-config:rstp-port-priority=64
# Mark a port as edge (connects to servers/VMs — skips learning delay)
sudo ovs-vsctl set port eth1 other-config:rstp-port-admin-edge=true
sudo ovs-vsctl set port eth1 other-config:rstp-port-auto-edge=true
MSTP for Multi-VLAN Environments
MSTP maps VLANs to separate spanning tree instances (MSTIs). VLAN 10 uses one physical path, VLAN 20 uses another — actual load distribution across redundant links, not just failover. In OVS, basic MSTP shares the rstp_enable flag with RSTP:
# Enable RSTP/MSTP
sudo ovs-vsctl set bridge br0 rstp_enable=true
# View current spanning tree topology
sudo ovs-appctl rstp/show br0
Full per-VLAN instance management in OVS typically needs an SDN controller behind it. For standalone OVS, RSTP handles the vast majority of cases without the added complexity.
Inspecting the Spanning Tree Topology
# Detailed RSTP state for all bridges
sudo ovs-appctl rstp/show
# Scoped to one bridge
sudo ovs-appctl rstp/show br0
# Full OVS config overview
sudo ovs-vsctl show
The rstp/show output tells you which bridge is root, port roles (root, designated, alternate), forwarding vs blocking state, and path costs. Start here when something looks wrong.
Practical Tips from the Trenches
1. Always Set Bridge Priority Explicitly
Default priority (32768) means MAC address picks the root — effectively random. Assign priorities deliberately so your most reliable switch always wins:
# Core bridge — root
sudo ovs-vsctl set bridge core-br other-config:rstp-priority=4096
# Distribution bridge — backup root
sudo ovs-vsctl set bridge dist-br other-config:rstp-priority=8192
# Access bridge — never root
sudo ovs-vsctl set bridge access-br other-config:rstp-priority=32768
2. Mark VM/Server Ports as Edge Ports
VM ports and server-facing ports won’t create loops — they connect to endpoints, not other switches. Mark them as edge ports and they skip the STP learning delay entirely, coming up immediately:
# Apply edge port config to all tap interfaces (VM ports)
for port in $(sudo ovs-vsctl list-ports br0 | grep tap); do
sudo ovs-vsctl set port "$port" other-config:rstp-port-admin-edge=true
sudo ovs-vsctl set port "$port" other-config:rstp-port-auto-edge=true
done
3. Watch for Topology Change Notifications
Frequent topology changes (TCNs) flush the MAC table and cause temporary flooding. When this keeps happening, something’s wrong — a flapping link, a misconfigured port, or a loop you haven’t caught yet. Watch the logs:
# Watch for STP/RSTP events in real time
sudo tail -f /var/log/openvswitch/ovs-vswitchd.log | grep -i "rstp\|stp\|topology"
# Summarize port roles and states
sudo ovs-appctl rstp/show br0 | grep -E "role|state|cost"
4. Test with a Deliberate Loop
The best sanity check: build a loop manually using veth pairs, then watch OVS block one of the ports on its own:
# Create two bridges
sudo ovs-vsctl add-br br-test1
sudo ovs-vsctl add-br br-test2
# Create two veth pairs (two paths between the bridges = a loop)
sudo ip link add veth1a type veth peer name veth1b
sudo ip link add veth2a type veth peer name veth2b
# Connect both pairs between the two bridges
sudo ovs-vsctl add-port br-test1 veth1a
sudo ovs-vsctl add-port br-test2 veth1b
sudo ovs-vsctl add-port br-test1 veth2a
sudo ovs-vsctl add-port br-test2 veth2b
# Enable RSTP on both
sudo ovs-vsctl set bridge br-test1 rstp_enable=true
sudo ovs-vsctl set bridge br-test2 rstp_enable=true
# Bring up all interfaces
sudo ip link set veth1a up; sudo ip link set veth1b up
sudo ip link set veth2a up; sudo ip link set veth2b up
# Wait a couple of seconds, then check
sudo ovs-appctl rstp/show br-test1
One veth port will show Alternate (blocking), the other Designated (forwarding). The physical loop still exists — STP broke it logically. That’s the confirmation you’re after.
Quick Reference: Common OVS STP Commands
# Enable/disable classic STP
sudo ovs-vsctl set bridge br0 stp_enable=true|false
# Enable/disable RSTP
sudo ovs-vsctl set bridge br0 rstp_enable=true|false
# Set bridge priority (multiple of 4096)
sudo ovs-vsctl set bridge br0 other-config:rstp-priority=4096
# Set port path cost
sudo ovs-vsctl set port eth0 other-config:rstp-path-cost=100
# Show spanning tree state
sudo ovs-appctl rstp/show [bridge-name]
# Check OVS version
sudo ovs-vswitchd --version
STP on OVS isn’t glamorous work. But set priority correctly, mark your VM ports as edge ports, run the loop test once to confirm it’s working, and it’ll quietly keep your network intact while you deal with more interesting problems.

