When physical switches are no longer enough
I once managed a lab environment with about 20 KVM virtual machines running across 3 physical hosts. At first, Linux Bridge worked fine — everything ran smoothly. But when I needed to segment the network by VM group (dev, staging, production), create VLANs to isolate traffic, or experiment with SDN, Linux Bridge started to fall short. Configuring VLANs had to be done manually one by one, there was no way to get a high-level view of the full network topology, and most importantly: you couldn’t monitor traffic between VMs.
That’s when I switched to Open vSwitch. Once you get the hang of it, it’s not nearly as intimidating as the enterprise-sounding name suggests.
Real problems with traditional Linux Bridge
Linux Bridge is perfectly adequate for simple use cases — connecting VMs to a LAN, sharing internet access. But as your infrastructure grows, you’ll quickly run into these issues:
- Manual and messy VLAN setup: You have to create individual sub-interfaces (
eth0.10,eth0.20…), with a separate bridge per VLAN. Managing 10 VLANs means 10 bridges — very hard to track. - No visibility: You have no idea where traffic is going or which VM is talking to which. Debugging requires guesswork with tcpdump.
- No SDN integration: Linux Bridge doesn’t support OpenFlow — the protocol used to program switch behavior from a central controller (like OpenDaylight, ONOS, or OpenStack’s Neutron).
- Complex bonding + VLAN: Combining multi-NIC bonding with VLANs on Linux Bridge is a lengthy configuration challenge.
If you only have 2–3 VMs, none of this matters. But when working with KVM/Proxmox at medium scale, or when you want to learn SDN/OpenStack, you need something more powerful.
What Open vSwitch is and why it’s different
Open vSwitch (OVS) is an open-source virtual switch that runs in the Linux kernel, designed specifically for virtualized environments. It fully supports VLANs (802.1Q), LACP bonding, tunneling (VXLAN, GRE, Geneve), OpenFlow, and has its own configuration database (OVSDB).
Unlike Linux Bridge, OVS manages everything through a unified CLI (ovs-vsctl). Configuration is automatically persisted via OVSDB — settings survive reboots. More importantly: OVS can be controlled by an SDN controller via the OpenFlow protocol.
Put simply: Linux Bridge is like an unmanaged switch you’d buy at a local electronics store. OVS is like a Cisco managed switch — with VLANs, monitoring, and full programmability.
Installing Open vSwitch on Ubuntu/Debian
Install packages
# Ubuntu 22.04 / Debian 12
sudo apt update
sudo apt install -y openvswitch-switch openvswitch-common
# Verify the service is running
sudo systemctl status openvswitch-switch
# Check version
ovs-vsctl --version
Install on CentOS/Rocky Linux
# Enable EPEL repo first
sudo dnf install -y epel-release
sudo dnf install -y openvswitch
sudo systemctl enable --now openvswitch
Creating your first virtual switch
Once installed, create an OVS bridge (equivalent to a Linux Bridge, but managed by OVS):
# Create an OVS bridge named br-ovs
sudo ovs-vsctl add-br br-ovs
# Add physical interface eth1 to the bridge
sudo ovs-vsctl add-port br-ovs eth1
# View the current configuration overview
sudo ovs-vsctl show
The output of ovs-vsctl show looks like this:
abc12345-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Bridge br-ovs
Port br-ovs
Interface br-ovs
type: internal
Port eth1
Interface eth1
ovs_version: "3.1.0"
Configuring VLANs on OVS
VLANs are where OVS really shows its advantage over Linux Bridge. I needed to separate 3 groups of VMs: VLAN 10 (dev), VLAN 20 (staging), VLAN 30 (production).
Access port — VM belongs to a single VLAN
# VM1 belongs to VLAN 10 (dev) — assign tag to port vnet0
sudo ovs-vsctl add-port br-ovs vnet0 tag=10
# VM2 belongs to VLAN 20 (staging)
sudo ovs-vsctl add-port br-ovs vnet1 tag=20
# VM3 belongs to VLAN 30 (production)
sudo ovs-vsctl add-port br-ovs vnet2 tag=30
Trunk port — uplink carrying all VLANs
# Port connecting to router/physical switch, allowing all VLANs through
sudo ovs-vsctl add-port br-ovs eth1 trunks=10,20,30
# Or allow all VLANs through (no restriction)
sudo ovs-vsctl set port eth1 trunks=[]
With this configuration, VMs in VLAN 10 cannot communicate with VMs in VLAN 20 — fully isolated at layer 2, no additional firewall rules needed.
When I need to quickly calculate subnets for each VLAN — for example, VLAN 10 using 192.168.10.0/24, VLAN 20 using 10.20.0.0/24 — I often use toolcraft.app/en/tools/developer/ip-subnet-calculator. Just enter the CIDR and it instantly shows the network range, broadcast address, and available host count — saves a lot of manual calculation.
Configuring Bonding (LACP) with OVS
This blog already has an article on Linux Network Bonding, so I won’t repeat the theory here. I’ll just cover how to do it with OVS — it’s different and much simpler.
Create a two-NIC bond with LACP
# Remove single port if previously added
sudo ovs-vsctl del-port br-ovs eth1
# Create a bond from eth1 + eth2 with LACP
sudo ovs-vsctl add-bond br-ovs bond0 eth1 eth2 \
bond_mode=balance-tcp \
lacp=active
# View bond status
sudo ovs-appctl bond/show bond0
OVS supports 3 bond modes, each suited to a different scenario:
- active-backup: 1 active, 1 standby. Simplest mode, no LACP support required from the switch. Automatic failover when a link goes down.
- balance-slb: Load balancing by source MAC. No LACP required, but only distributes outbound traffic.
- balance-tcp: Flow-based load balancing (src/dst IP + port). Most efficient — can fully utilize both NICs’ bandwidth — but requires the upstream switch to support LACP 802.3ad.
Tunneling: Connecting OVS across multiple hosts
My favorite feature is tunneling. With 3 physical hosts each running OVS, I wanted VMs on host A to communicate with VMs on host B as if they were on the same switch — without any configuration changes on the physical switch.
The solution: use VXLAN tunnels between OVS instances.
# On Host A (IP: 192.168.1.10)
# Create a tunnel to Host B (192.168.1.20)
sudo ovs-vsctl add-port br-ovs vxlan-to-hostB \
-- set interface vxlan-to-hostB \
type=vxlan \
options:remote_ip=192.168.1.20 \
options:key=100
# On Host B (IP: 192.168.1.20)
# Create the reverse tunnel back to Host A
sudo ovs-vsctl add-port br-ovs vxlan-to-hostA \
-- set interface vxlan-to-hostA \
type=vxlan \
options:remote_ip=192.168.1.10 \
options:key=100
Once the tunnels are set up, VMs in the same VLAN on different hosts can ping each other over the overlay network. Traffic is encapsulated in UDP port 4789 and travels across the physical network as a normal packet — the physical switch has no knowledge of the inner VLANs.
Useful debug commands for everyday use
# View the full OVS configuration
sudo ovs-vsctl show
# View the flow table (active OpenFlow rules)
sudo ovs-ofctl dump-flows br-ovs
# View port statistics (packets, bytes, errors)
sudo ovs-ofctl dump-ports br-ovs
# View status of each interface
sudo ovs-vsctl list interface
# Delete all flows (reset to default forwarding)
sudo ovs-ofctl del-flows br-ovs
# Mirror traffic from one port to another (for capture)
sudo ovs-vsctl -- set Bridge br-ovs mirrors=@m \
-- --id=@vnet0 get Port vnet0 \
-- --id=@mirror_port get Port eth-monitor \
-- --id=@m create Mirror name=monitor \
select-dst-port=@vnet0 \
select-src-port=@vnet0 \
output-port=@mirror_port
The traffic mirroring command is particularly useful for debugging: it clones all traffic from a VM to another interface, allowing Wireshark to capture it without affecting the running VM in any way.
SDN integration with OpenFlow
If you’re learning OpenStack, Kubernetes networking, or want to experiment with an SDN controller — OVS supports OpenFlow out of the box. Here’s how to connect OVS to an SDN controller:
# Connect br-ovs to an OpenDaylight/ONOS controller
sudo ovs-vsctl set-controller br-ovs tcp:192.168.1.100:6653
# Check the controller connection status
sudo ovs-vsctl get-controller br-ovs
# Manually add an OpenFlow rule (example: drop traffic from a specific MAC)
sudo ovs-ofctl add-flow br-ovs \
"in_port=1,dl_src=aa:bb:cc:dd:ee:ff,actions=drop"
# Verify the rule was added
sudo ovs-ofctl dump-flows br-ovs
Without a controller, OVS operates as a normal switch (fail-open mode). When a controller connects, it pushes flow rules down and takes full control over the switch’s behavior.
Best approach for each use case
After roughly 2 years of using OVS in my lab, here’s how I choose between options for different scenarios:
- Just need a simple bridge for KVM/libvirt? Linux Bridge is still sufficient. Don’t overcomplicate it.
- Need VLAN isolation between multiple VMs? OVS with tagged ports — much faster to configure than bridge + VLAN interfaces.
- Multi-host VM networking? OVS + VXLAN tunneling. No need for physical switch VLAN support, no hardware configuration required.
- Learning OpenStack/SDN? OVS is essential. The Neutron OVS driver is the default in many OpenStack distributions.
- Need high bandwidth + redundancy? OVS bond with balance-tcp + LACP — gets you both without complex configuration.
OVS has an initial learning curve, but once you understand its bridge/port/interface model, the configuration logic becomes very intuitive. The hardest part is usually debugging conflicting flow rules — at that point, ovs-ofctl dump-flows and ovs-appctl fdb/show are your two lifesavers.

