OpenStack Neutron DVR: Moving L3 Routing to Compute Nodes Without the Network-Node Bottleneck
Neutron DVR moves L3 routing to compute nodes, eliminating the east-west traffic bottleneck. Learn the data-plane changes, OVS/OVN configuration, and the control-plane trade-offs for scaling.
22 Aug 2025, 13:46 UTC

The Problem: Centralized Routing Doesn't Scale
In a traditional OpenStack deployment, every tenant router lives in a single namespace on a network node. All east-west traffic between VMs on different compute nodes, plus all north-south traffic to the internet, must traverse that network node. At modest scale this works; beyond a few hundred compute nodes the network node becomes a throughput ceiling and a single point of contention.
Neutron Distributed Virtual Router (DVR) solves this by running the L3 agent in dvr mode on every compute node. Each compute hosts a local router namespace with interfaces on the tenant and provider networks, so VM-to-VM and VM-to-external flows route locally. Only SNAT (Source Network Address Translation)—used for external egress—stays centralized on nodes running dvr_snat.
How DVR Changes the Data Plane
When you create a router with router_distributed=True, neutron-server pushes the router configuration to the L3 agent on every compute that has a port on that router. The agent spawns a qrouter-<uuid> namespace containing:
- An interface on each tenant subnet (acting as the gateway IP for that subnet)
- An interface on the external provider network (flat or VLAN)
- NAT rules for floating-IP DNAT (Destination NAT) for ingress traffic when the VM port has a floating IP
East-west traffic between two VMs on different computes now takes a single hop: VM A → local qrouter → VXLAN/GENEVE tunnel → VM B. No network-node hop. North-south egress still uses the centralized SNAT namespace on a dvr_snat node, but ingress (DNAT) is distributed when the floating IP is bound to a port on a compute running the DVR agent.
Prerequisites and Configuration
DVR requires:
- ML2 with a mechanism driver that supports DVR (Open vSwitch, OVN, or Linux Bridge)
- Kernel ≥ 3.13 for network namespaces
- L2 population enabled so computes learn remote VTEP/MAC mappings without flood-and-learn
- External network as provider-flat or provider-vlan; overlays work with MTU ≥ 1550 for VXLAN
On each compute node, set in /etc/neutron/l3_agent.ini:
[DEFAULT]
agent_mode = dvr
interface_driver = openvswitch # or ovn, linuxbridge
external_network_bridge = # leave empty; use provider networks
On two or more network nodes for HA, set:
agent_mode = dvr_snat
Then create tenant routers as distributed:
openstack router create --distributed myrouter
Worked Example: Verifying Distributed Routing
After deployment, confirm the agent modes using the OpenStack CLI:
openstack network agent list --agent-type l3
# Expect: computes show 'dvr', network nodes show 'dvr_snat'
On a compute node hosting a VM on the router, list namespaces to verify the agent created the local router:
ip netns | grep qrouter
# qrouter-<router-uuid> should exist
Inside that namespace, check connected subnets to ensure the local gateway is active:
ip netns exec qrouter-<uuid> ip route
# Shows direct connected routes for each tenant subnet
From a VM, trace to another tenant VM on a different compute to confirm the path avoids the network node:
tracepath <other-vm-ip>
# Single hop to gateway MAC on local compute
Validate L2 population (OVS example) to ensure the compute knows where the remote VM resides:
ovs-vsctl list-openflow-rules br-tun | grep -i learn
# Should show remote VTEP/MAC entries for ports on other computes
Trade-offs and Limitations
Control-plane load: Every compute runs an L3 agent. OVS flow tables grow with router count. Neutron-server must push router updates to all computes via RPC. Deployments beyond ~500 computes often hit neutron-server bottlenecks; consider OVN native routing instead.
IPv6 gaps: DVR handles SLAAC and RA, but prefix delegation (PD) and distributed SNAT for IPv6 are not fully implemented in the OVS driver. The OVN driver has better parity but requires ovn-controller on computes.
Debugging complexity: Flows are distributed. tcpdump must be taken on the correct compute node. neutron-debug probe only works from a network node. Correlate logs across L3 agents using router ID and port IDs.
Upgrade constraints: Rolling upgrades across mixed DVR/non-DVR computes are not supported. Coordinate agent restarts.
When to Adopt DVR
Enable DVR when east-west traffic dominates (such as multi-tier apps or service meshes) and you have 3+ compute nodes where eliminating the network-node bottleneck is a priority. Stick with centralized routing for small deployments (< 50 computes) where operational simplicity outweighs scaling, or when IPv6 prefix delegation is a hard requirement.
Start by enabling dvr=True on a subset of computes in a staging environment, validate the verification steps above, then roll out incrementally. The scaling payoff is real, but so is the operational surface area.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.