This is one half of a two-part series. This document covers Open vSwitch (OVS) — a programmable software switch. The companion document, Linux Network Namespaces: A Deep Dive, covers the kernel primitive most often used with OVS to create the isolated endpoints it switches between. The two intersect most directly in Section 10 below, and in that document's Section 3.4.
A third, hands-on companion — "Getting Started with Netns and OVS: A Workshop Guide" — walks through both of these as guided labs if you'd rather learn by typing commands than reading theory.
Table of Contents¶
- Introduction & Mental Model
- Open vSwitch: Architecture
- OVS Bridges, Ports, and Interface Types
- OpenFlow and the Flow Table Model
- VLANs and Trunking in OVS
- Tunneling: VXLAN, GRE, Geneve
- Combining Namespaces with OVS
- OVSDB, OVN, and Higher-Level Control
- Performance: Kernel Datapath vs Userspace vs DPDK
- Observability and Troubleshooting
- Security Considerations
- Real-World Contexts (Kubernetes, OpenStack, CI)
- Command Reference / Cheat Sheet
- Further Reading
1. Introduction & Mental Model¶
Open vSwitch (OVS) is a production-quality, programmable software switch that replaces (or augments) the humble Linux bridge. It understands OpenFlow, VLANs, tunnels, QoS, and can be driven by a central controller (SDN) instead of static configuration.
OVS answers the question "how do I wire many isolated network endpoints together, tag/route/tunnel their traffic, and do it at scale with policy?" It assumes those endpoints already exist as something with a network interface to plug in — most commonly a VM's tap device, a container's veth end, or, for learning purposes, a Linux network namespace's veth end. If you haven't worked with namespaces yet, the companion document Linux Network Namespaces: A Deep Dive covers that isolation primitive from the ground up and is a natural prerequisite before Section 7 of this guide, which wires the two together.
This document treats OVS as a self-contained topic: its architecture, how to build bridges and ports, how OpenFlow's flow-table model works, VLANs and tunnels, and where OVS fits in real systems like Kubernetes and OpenStack.
2. Open vSwitch: Architecture¶
OVS is not a kernel module wearing a trench coat over the Linux bridge — it's a full software switch stack with userspace daemons, a database, and (optionally) a kernel fast-path.
2.1 The core components¶
| Component | Role |
|---|---|
ovsdb-server |
Stores switch configuration (bridges, ports, interfaces, controllers, QoS, mirrors) in OVSDB, a JSON-RPC document database. This is the "source of truth." |
ovs-vswitchd |
The actual switching daemon. Reads config from ovsdb-server, programs the datapath, handles OpenFlow, computes flow decisions. |
Datapath (kernel module openvswitch.ko, or userspace DPDK datapath) |
The fast path that actually forwards packets once a flow decision has been cached. |
ovs-vsctl |
CLI for reading/writing OVSDB — bridges, ports, interfaces. Your main day-to-day config tool. |
ovs-ofctl |
CLI for talking OpenFlow directly to ovs-vswitchd — dump/add/delete flows. |
ovs-dpctl |
Low-level datapath inspection (rarely needed day to day). |
ovs-appctl |
Swiss-army-knife control socket for runtime introspection/debugging of ovs-vswitchd. |
2.2 The two forwarding paths: "slow path" and "fast path"¶
The first packet of a new flow (defined by a tuple of headers) is punted from the kernel datapath up to ovs-vswitchd in userspace — this is the "slow path," where the full OpenFlow flow table is evaluated (possibly involving many table lookups, learning actions, etc.). The result of that decision is then cached as a compact megaflow entry directly in the kernel datapath. Every subsequent packet matching that cached entry is forwarded entirely in-kernel — the "fast path" — without touching userspace again. This is the single most important performance idea in OVS: you pay the OpenFlow-table-walk cost once per flow, not once per packet.
2.3 Installing OVS¶
# Debian/Ubuntu
sudo apt install openvswitch-switch
# RHEL/Fedora/CentOS Stream
sudo dnf install openvswitch
sudo systemctl enable --now openvswitch
Verify it's alive:
sudo ovs-vsctl show
sudo systemctl status openvswitch-switch # or 'openvswitch' on RHEL family
3. OVS Bridges, Ports, and Interface Types¶
3.1 Creating a bridge¶
sudo ovs-vsctl add-br br0
sudo ovs-vsctl show
An OVS bridge is not a kernel network device the same way a Linux bridge is, though it does appear in ip link output as a device of type openvswitch — you can ip addr add to it, bring it up/down, etc., same as any interface.
3.2 Adding ports¶
# Attach an existing veth end (e.g., the root-side of a pair going into a namespace —
# see the namespaces guide's section on veth pairs: linux-network-namespaces-guide.md#3-connecting-namespaces-veth-pairs-and-bridges)
sudo ovs-vsctl add-port br0 veth-red-br
# Create an OVS "internal" port — a virtual NIC that lives on the bridge itself
sudo ovs-vsctl add-port br0 br0-int -- set interface br0-int type=internal
sudo ip addr add 192.168.50.1/24 dev br0-int
sudo ip link set br0-int up
Internal ports are OVS's answer to "I want an IP address directly reachable through this switch, without a separate veth." They're commonly used for the bridge's own management IP, or as one leg of connectivity into a namespace (an internal port can be moved into a namespace just like a veth end can).
3.3 Interface types at a glance¶
| Type | Purpose |
|---|---|
system (default) |
A normal kernel netdev — physical NIC, veth end, TAP device, etc. |
internal |
A virtual NIC owned by OVS itself, useful for bridge-local addressing or feeding a namespace. |
patch |
Connects two OVS bridges together directly inside the same host, without a veth. |
vxlan, gre, geneve, stt |
Tunnel port types — encapsulate/decapsulate at L2/L3 boundaries. Covered in Section 6. |
tap |
Classic TAP device, often used for VM connectivity (QEMU). |
3.4 Removing bridges and ports¶
sudo ovs-vsctl del-port br0 veth-red-br
sudo ovs-vsctl del-br br0
3.5 Putting a namespace on an OVS bridge¶
Exactly analogous to the Linux-bridge pattern in the namespaces guide, but with ovs-vsctl add-port instead of ip link set master:
sudo ip netns add red
sudo ip link add veth-red type veth peer name veth-red-ovs
sudo ip link set veth-red netns red
sudo ovs-vsctl add-port br0 veth-red-ovs
sudo ip link set veth-red-ovs up
sudo ip netns exec red ip link set veth-red up
sudo ip netns exec red ip link set lo up
sudo ip netns exec red ip addr add 10.10.10.1/24 dev veth-red
By default, a freshly created OVS bridge behaves like an ordinary learning L2 switch (MAC learning, flood-unknown, normal forwarding) — this is the NORMAL action in its default flow table, described next.
4. OpenFlow and the Flow Table Model¶
This is the conceptual leap from "a switch" to "a programmable switch."
4.1 What a flow is¶
An OpenFlow flow entry has three parts:
- Match — a set of header fields to match against (in-port, src/dst MAC, VLAN tag, src/dst IP, protocol, TCP/UDP ports, etc.)
- Priority — higher-priority entries are evaluated first when multiple entries could match
- Actions — what to do: forward out a port, drop, modify a header, push/pop a VLAN tag, resubmit to another table, or fall back to
NORMAL(standard L2 learning-switch behavior)
4.2 Viewing the current flow table¶
sudo ovs-ofctl dump-flows br0
On a freshly created bridge you'll typically see one catch-all entry: priority=0,actions=NORMAL — "if nothing more specific matches, just act like a normal switch." Everything more advanced is built by adding higher-priority, more specific rules above that baseline.
4.3 Adding flows manually¶
# Drop all traffic from a specific MAC
sudo ovs-ofctl add-flow br0 "priority=100,dl_src=00:11:22:33:44:55,actions=drop"
# Forward ICMP from port 1 to port 2, drop everything else from port 1
sudo ovs-ofctl add-flow br0 "priority=200,in_port=1,icmp,actions=output:2"
sudo ovs-ofctl add-flow br0 "priority=100,in_port=1,actions=drop"
# Redirect all TCP port 80 traffic to a monitoring port (mirror-like via output)
sudo ovs-ofctl add-flow br0 "priority=150,tcp,tp_dst=80,actions=output:3,output:2"
4.4 Ports and numbers¶
OpenFlow refers to ports by number, not name. Look them up with:
sudo ovs-ofctl show br0
4.5 Multiple flow tables and resubmit¶
Real OVS deployments (and OVN under the hood) use dozens of flow tables chained via resubmit(,<table>) actions — a pipeline where table 0 might do ingress ACLs, table 10 might do MAC learning, table 20 might do routing, etc. This is exactly how OVN implements distributed logical routers/switches purely in flow rules. As a beginner you'll mostly work in table 0; understanding that this scales to a full pipeline is what lets you read OVN/Neutron flow dumps later without panicking.
4.6 Deleting flows and resetting to defaults¶
sudo ovs-ofctl del-flows br0 # wipe everything
sudo ovs-ofctl add-flow br0 "priority=0,actions=NORMAL" # restore default behavior
4.7 Static config vs. controller-driven¶
Everything above is "static" flow programming via ovs-ofctl — you, the human, are the controller. In SDN deployments, an external controller (an OpenFlow controller, or OVN's ovn-controller) maintains a persistent OpenFlow connection to ovs-vswitchd and pushes/updates flows dynamically in response to topology changes. You can point a bridge at a controller with:
sudo ovs-vsctl set-controller br0 tcp:192.0.2.10:6653
5. VLANs and Trunking in OVS¶
5.1 Access ports (untagged, single VLAN)¶
sudo ovs-vsctl set port veth-red-ovs tag=10
Frames arriving untagged on this port are internally tagged VLAN 10 for switching purposes; frames leaving the port have the tag stripped. This is the "access port" model familiar from physical switches.
5.2 Trunk ports (multiple VLANs, tagged)¶
sudo ovs-vsctl set port uplink0 trunks=10,20,30
A trunk port passes tagged frames for the listed VLANs through untouched — used for uplinks to physical switches or between bridges carrying multiple tenant networks.
5.3 Native/VLAN mode nuance¶
OVS supports vlan_mode (access, trunk, native-tagged, native-untagged) for finer control over how the "native" (untagged) VLAN on a trunk is handled — relevant when interoperating with physical switches that have specific native-VLAN expectations.
sudo ovs-vsctl set port uplink0 vlan_mode=native-untagged
5.4 Verifying VLAN behavior¶
sudo ovs-vsctl list port veth-red-ovs
sudo ovs-appctl fdb/show br0 # MAC/VLAN learning table
6. Tunneling: VXLAN, GRE, Geneve¶
Tunnels let OVS stretch an L2 segment across L3-routed infrastructure — the backbone of multi-host overlay networking (this is exactly how Kubernetes CNI overlays and OpenStack tenant networks span multiple hosts).
6.1 VXLAN port example¶
sudo ovs-vsctl add-port br0 vxlan0 -- set interface vxlan0 type=vxlan \
options:remote_ip=203.0.113.20 options:key=100
This creates a VXLAN tunnel endpoint bridged into br0, encapsulating traffic toward 203.0.113.20 with VXLAN Network Identifier (VNI) 100. Anything an OVS bridge would normally switch out this port instead gets UDP/VXLAN-encapsulated and routed at L3 to the remote host, where a matching bridge/port decapsulates it.
6.2 GRE and Geneve¶
sudo ovs-vsctl add-port br0 gre0 -- set interface gre0 type=gre options:remote_ip=203.0.113.20
sudo ovs-vsctl add-port br0 gnv0 -- set interface gnv0 type=geneve options:remote_ip=203.0.113.20 options:key=100
Geneve is the modern preferred choice in most new deployments (including OVN) because its header is extensible (TLV options), which lets control planes carry extra metadata (e.g., logical network/security context) without inventing a new encapsulation format.
6.3 When tunnels matter¶
- Multi-host overlay networks where you don't control the underlying L3 fabric's VLAN configuration
- Multi-tenant isolation without consuming physical VLAN ID space (4096 VLAN limit vs. 16-million VXLAN VNI space)
- Kubernetes overlay CNIs (Flannel VXLAN backend, OVN-Kubernetes, Cilium's VXLAN mode) and OpenStack Neutron's ML2/OVN or ML2/OVS VXLAN tenant networks all use this exact mechanism.
7. Combining Namespaces with OVS¶
This is where OVS meets the isolation primitive covered in the companion Linux Network Namespaces: A Deep Dive. If veth pairs and ip netns exec are unfamiliar, read that document's Sections 2–3 first — everything below assumes that background.
A realistic small lab topology: two "tenant" namespaces on VLAN 10, one "tenant" namespace on VLAN 20, all switched by one OVS bridge, with an internal port giving the bridge itself a management IP.
sudo ovs-vsctl add-br br0
# Namespace 'web' -> VLAN 10
sudo ip netns add web
sudo ip link add veth-web type veth peer name veth-web-ovs
sudo ip link set veth-web netns web
sudo ovs-vsctl add-port br0 veth-web-ovs tag=10
sudo ip link set veth-web-ovs up
sudo ip netns exec web ip link set lo up
sudo ip netns exec web ip link set veth-web up
sudo ip netns exec web ip addr add 10.0.10.1/24 dev veth-web
# Namespace 'db' -> VLAN 10 (same segment as web)
sudo ip netns add db
sudo ip link add veth-db type veth peer name veth-db-ovs
sudo ip link set veth-db netns db
sudo ovs-vsctl add-port br0 veth-db-ovs tag=10
sudo ip link set veth-db-ovs up
sudo ip netns exec db ip link set lo up
sudo ip netns exec db ip link set veth-db up
sudo ip netns exec db ip addr add 10.0.10.2/24 dev veth-db
# Namespace 'mgmt' -> VLAN 20 (isolated from web/db at L2)
sudo ip netns add mgmt
sudo ip link add veth-mgmt type veth peer name veth-mgmt-ovs
sudo ip link set veth-mgmt netns mgmt
sudo ovs-vsctl add-port br0 veth-mgmt-ovs tag=20
sudo ip link set veth-mgmt-ovs up
sudo ip netns exec mgmt ip link set lo up
sudo ip netns exec mgmt ip link set veth-mgmt up
sudo ip netns exec mgmt ip addr add 10.0.20.1/24 dev veth-mgmt
web and db can reach each other (same VLAN, same bridge, default NORMAL flow). mgmt cannot reach either without a router — exactly like real VLAN segmentation. This mirrors, at toy scale, what a CNI plugin or Neutron ML2/OVS agent is doing per-host: namespaces (pods/VMs) as endpoints, OVS as the local switch fabric, VLAN tags or tunnels as the isolation/segmentation mechanism.
7.1 Adding inter-VLAN routing¶
Give the bridge itself (via an internal port with 802.1Q sub-interfaces, or a separate router namespace with two tagged veth legs — see the namespaces guide's routing section) an address on each VLAN, enable forwarding, and you've built a router-on-a-stick entirely in software — a genuinely useful pattern for practicing routing concepts without hardware.
8. OVSDB, OVN, and Higher-Level Control¶
Hand-crafting flows with ovs-ofctl does not scale past a handful of rules on a handful of hosts. Real deployments layer higher-level control planes on top:
- OVN (Open Virtual Network) — a project from the same community as OVS. You describe logical switches, logical routers, ACLs, and load balancers in a high-level northbound database (
OVN_Northbound);ovn-northdcompiles that into logical flows inOVN_Southbound;ovn-controlleron each hypervisor translates those into the actual OpenFlow rules programmed into localovs-vswitchd. This is the mechanism behind OVN-Kubernetes and OpenStack's ML2/OVN driver. - Kubernetes CNI plugins (OVN-Kubernetes, Antrea) use OVS as their per-node dataplane, programming flows/OVSDB from Go controllers reacting to Kubernetes API events (Pod/Service/NetworkPolicy objects) rather than by hand. Each Pod's networking still boils down to a network namespace — see the companion guide — with OVS as the fabric around it.
- OpenStack Neutron (ML2/OVS or ML2/OVN mechanism drivers) does the equivalent for VM networking — tenant networks, security groups (implemented as flow rules or conntrack-based ACLs), floating IPs, and routers all ultimately become OVS bridges, ports, and flows on compute/network nodes.
You don't need OVN to learn OVS, but recognizing that "someone else's controller is just doing ovs-vsctl/ovs-ofctl-equivalent operations on your behalf, faster and more consistently than you could by hand" demystifies a lot of container/cloud networking internals.
9. Performance: Kernel Datapath vs. Userspace vs. DPDK¶
- Kernel datapath (default) —
openvswitch.kohandles the fast path in-kernel; good general-purpose performance, works everywhere, no special hardware/driver requirements. - Userspace datapath — a pure-userspace fast path, useful in environments where the kernel module isn't available (some containerized deployments) or where DPDK is in play.
- OVS-DPDK — pins CPU cores, uses poll-mode NIC drivers (bypassing the kernel network stack entirely) for very high packet-per-second throughput, at the cost of dedicating cores and needing DPDK-compatible NICs. Common in telco/NFV deployments and high-throughput OpenStack compute nodes. This is an advanced, hardware-dependent topic outside the scope of a getting-started guide, but worth knowing exists when you see
ovs-vsctl set Open_vSwitch . other_config:dpdk-init=truein the wild.
For homelab and learning purposes, the kernel datapath (the default you get from a package-manager install) is exactly what you want — DPDK adds operational complexity with no benefit at that scale.
10. Observability and Troubleshooting¶
This section covers OVS-side troubleshooting. For namespace-side diagnostics (interfaces, routes, sockets inside a namespace), see the namespaces guide's Observability and Troubleshooting section.
sudo ovs-vsctl show # overall bridge/port/interface topology
sudo ovs-ofctl show br0 # OpenFlow port list and features
sudo ovs-ofctl dump-flows br0 # what rules are actually installed
sudo ovs-appctl fdb/show br0 # MAC learning table (like a bridge's fdb)
sudo ovs-appctl bond/show # bonding status, if applicable
sudo ovs-dpctl dump-flows # kernel datapath megaflow cache (the "fast path" cache itself)
Tracing what a specific packet would do¶
ovs-appctl ofproto/trace is invaluable — it simulates a packet through the flow table without sending real traffic, telling you exactly which rules matched and why:
sudo ovs-appctl ofproto/trace br0 in_port=1,dl_src=00:11:22:33:44:55,dl_dst=ff:ff:ff:ff:ff:ff
Packet capture across the boundary¶
tcpdump works fine on OVS ports from the root namespace, since OVS ports are still visible as normal netdevs to the kernel's packet-capture hooks. For visibility purely inside the OVS pipeline (e.g., after VLAN tagging, before a tunnel encapsulation), ovs-appctl ofproto/trace and port mirroring (ovs-vsctl mirror tables) are more precise than tcpdump on an endpoint.
Common beginner pitfalls¶
- Mismatched VLAN tags between two ports that are supposed to be on the same segment
- Expecting an OVS bridge with no controller and default flows to not act like an ordinary switch — it does, via the
NORMALaction, until you add more specific rules - Adding a physical NIC directly to an OVS bridge without first moving its IP configuration to the bridge/an internal port — this can silently disconnect a remote SSH session
11. Security Considerations¶
- OVS flow rules are your firewall when you drop below
NORMAL. Once you start writing explicit flows, remember there's no implicit "deny by default except what I allowed" unless you write it that way — a catch-alldropat low priority beneath your explicitallowrules is the safe pattern. - Conntrack-based ACLs (
ctmatch/action in OVS flows) let you build genuinely stateful firewalling in the flow table itself — this is how OpenStack security groups and Kubernetes NetworkPolicies are frequently implemented at the OVS layer rather than via iptables. - Tunnel traffic (VXLAN/GRE/Geneve) is unencrypted by default. If tunnels cross untrusted L3 paths, put them over IPsec or a WireGuard underlay, or use OVS's IPsec integration where available.
OVS sits downstream of whatever isolation the endpoints themselves provide — see the namespaces guide's Security Considerations for the isolation-primitive side of that boundary.
12. Real-World Contexts (Kubernetes, OpenStack, CI)¶
- Kubernetes: OVN-Kubernetes and Antrea use OVS bridges per node, with flows programmed by their respective controllers reacting to the Kubernetes API. Each pod endpoint behind those flows is still a network namespace.
- OpenStack Neutron: compute nodes run an OVS agent that wires VM tap interfaces into an integration bridge (
br-int), with VLAN tags or tunnel ports providing tenant network isolation, and a separate provider/external bridge (br-ex) handling north-south traffic. - Home labs: exactly what the workshop companion document walks through — a safe, disposable playground to internalize these concepts on a single machine or single VM, with zero risk to production infrastructure, before touching them in Kubernetes/OpenStack contexts where mistakes are more consequential.
13. Command Reference / Cheat Sheet¶
OVS bridge / port management¶
ovs-vsctl add-br <br>
ovs-vsctl del-br <br>
ovs-vsctl add-port <br> <iface> [tag=<vlan>] [trunks=<v1>,<v2>]
ovs-vsctl del-port <br> <iface>
ovs-vsctl show
ovs-vsctl list port <iface>
ovs-vsctl set-controller <br> tcp:<ip>:<port>
OVS OpenFlow / flows¶
ovs-ofctl show <br>
ovs-ofctl dump-flows <br>
ovs-ofctl add-flow <br> "priority=<n>,<match>,actions=<action>"
ovs-ofctl del-flows <br>
OVS diagnostics¶
ovs-appctl fdb/show <br>
ovs-appctl ofproto/trace <br> <packet-fields>
ovs-dpctl dump-flows
ovs-appctl bond/show
For namespace and veth commands (the endpoints these bridges typically connect to), see the namespaces guide's cheat sheet.
14. Further Reading¶
man 8 ovs-vswitchd,man 8 ovs-vsctl,man 8 ovs-ofctl,man 8 ovs-fields— the man pages are genuinely excellent and authoritative for exact syntax.- Open vSwitch upstream documentation (
openvswitch.org) — architecture papers and the OVS FAQ are worth reading once the basics from this guide feel comfortable. - OVN documentation, for the logical-network abstraction layer built on top of OVS.
- See Linux Network Namespaces: A Deep Dive for the isolation primitive these bridges most often connect.
- For hands-on practice, work through the companion document: "Getting Started with Netns and OVS: A Workshop Guide."