Linux VRF Explained: Build Isolated Routing Tables with iproute2
A Linux server can have multiple network interfaces, multiple gateways, and multiple routing tables.
At first, ordinary routing may be enough.
As the network grows, you may introduce source-based routing with ip rule. That works extremely well for many multi-homed systems, and CorgiNetwork’s Linux Policy Routing with ip rule and Multiple Routing Tables guide explains that architecture in detail.
But eventually you may need something stronger:
- separate Layer 3 routing domains;
- different default gateways for different services or tenants;
- multiple interfaces that should use independent routing tables;
- management traffic isolated from production routing;
- routing daemons operating in separate routing domains;
- overlapping or complex multi-tenant network designs.
This is where Linux VRF — Virtual Routing and Forwarding — becomes extremely useful.
A Linux VRF associates a virtual network device with a routing table. Physical or virtual interfaces can then be attached to that VRF, allowing their Layer 3 traffic to use the routing domain associated with it.
The official Linux kernel documentation describes the Linux implementation as VRF-lite and specifically identifies multi-tenancy with independent routing tables and different default gateways as one of its use cases.
This guide builds two VRFs from scratch, explains l3mdev routing rules, shows how to run applications inside a VRF, demonstrates packet captures and route lookups, and compares VRF with policy routing, VLANs, and network namespaces
What Is Linux VRF?
VRF stands for:
Virtual Routing and Forwarding.
The basic concept is simple.
Instead of having every interface depend primarily on one shared routing domain:
Linux
|
Main Routing Table
/ \
eth1 eth2
we can separate routing:
Linux
/ \
VRF BLUE VRF RED
| |
Table 100 Table 200
| |
eth1 eth2
Now each side has its own Layer 3 routing context.
The ip-vrf(8) documentation describes a VRF as providing Layer 3 traffic isolation, analogous to how VLANs provide Layer 2 isolation. Fundamentally, each VRF is associated with a separate routing table.
This distinction is important:
VLAN
↓
Layer 2 segmentation
VRF
↓
Layer 3 routing separation
Why Linux VRF Exists
Consider a server with two networks.
Management Network
|
enp1s0
|
Linux
|
enp2s0
|
Production Network
The management network has:
10.10.10.20/24
Gateway 10.10.10.1
Production has:
172.16.20.20/24
Gateway 172.16.20.1
You could solve this using policy routing:
Source 10.10.10.20
↓
ip rule
↓
Table 100
and:
Source 172.16.20.20
↓
ip rule
↓
Table 200
That architecture is covered in our Linux Policy Routing guide.
VRF provides another model:
enp1s0
↓
VRF MANAGEMENT
↓
Table 100
enp2s0
↓
VRF PRODUCTION
↓
Table 200
The interface itself becomes associated with a VRF routing domain.
Linux VRF vs Policy Routing
These technologies are related, not mutually exclusive.
Policy Routing
Policy routing asks:
Does packet match rule?
↓
Which table should be consulted?
For example:
ip rule add from 10.10.10.20 table 100
This works exceptionally well when you need routing decisions based on:
- source address;
- destination;
- firewall mark;
- incoming interface;
- other routing-policy selectors.
VRF
VRF instead organizes interfaces and traffic into Layer 3 routing domains:
Interface
↓
VRF
↓
Routing Table
Interestingly, Linux VRF itself integrates with the routing-policy system.
The kernel documentation states that an l3mdev FIB rule directs route lookups to the routing table associated with the VRF device.
So Linux VRF does not eliminate policy routing.
It builds on the same routing infrastructure.
Linux VRF vs VLAN
VRF and VLAN solve different problems.
A VLAN primarily provides:
Layer 2 segmentation
A VRF provides:
Layer 3 routing separation
You can therefore combine them:
Physical NIC
|
VLAN 10
|
VRF BLUE
|
Routing Table 100
and:
Physical NIC
|
VLAN 20
|
VRF RED
|
Routing Table 200
This combination is common in more sophisticated network architectures.
Linux VRF vs Network Namespace
Linux network namespaces provide broader network-stack isolation.
A namespace can have its own:
- interfaces;
- routing tables;
- firewall context;
- sockets;
- network configuration.
VRF is narrower.
The kernel documentation notes that VRF affects Layer 3 and above, while Layer 2 tools do not need to be separately run in each VRF. It also explicitly supports putting VRFs inside network namespaces.
Think of the difference as:
VRF
↓
Routing-domain separation
versus:
Network Namespace
↓
Broader network-stack isolation
Neither is universally superior.
The appropriate tool depends on what you need to isolate.
Build a Practical Linux VRF Lab
We will create:
Linux Server
/ \
VRF BLUE VRF RED
| |
Table 100 Table 200
| |
enp1s0 enp2s0
| |
192.168.10.1 10.20.30.1
Gateway Gateway
Server addresses:
enp1s0 = 192.168.10.20/24
enp2s0 = 10.20.30.20/24
We want complete routing separation:
BLUE
192.168.10.20
↓
Table 100
↓
192.168.10.1
RED
10.20.30.20
↓
Table 200
↓
10.20.30.1
Step 1: Inspect the Existing Network
Before changing anything:
ip -br link
Then:
ip -br addr
Check routes:
ip route
Check policy rules:
ip rule
And finally:
ip route show table all
Do this before creating a VRF.
You need a baseline.
If the server already routes through an unexpected NIC, diagnose that first with our Linux Traffic Uses the Wrong Interface troubleshooting guide.
Step 2: Create VRF BLUE
Create a VRF device associated with routing table 100:
sudo ip link add vrf-blue type vrf table 100
Bring it up:
sudo ip link set vrf-blue up
Inspect it:
ip -d link show vrf-blue
The key relationship is:
vrf-blue
↓
table 100
The official kernel configuration syntax follows this same model:
ip link add vrf-blue type vrf table 10
ip link set dev vrf-blue up
with the VRF device explicitly associated with a FIB table.
Step 3: Create VRF RED
Now create:
sudo ip link add vrf-red type vrf table 200
Bring it up:
sudo ip link set vrf-red up
We now have:
vrf-blue → Table 100
vrf-red → Table 200
List VRFs with:
ip vrf show
The ip vrf command also supports inspecting VRFs, identifying the VRF associated with a process, listing processes and executing commands within a VRF context.
Step 4: Attach an Interface to VRF BLUE
Attach:
enp1s0
to:
vrf-blue
using:
sudo ip link set dev enp1s0 master vrf-blue
Now:
enp1s0
↓
vrf-blue
↓
Table 100
Check:
ip link show master vrf-blue
The kernel documentation calls this enslaving the interface to the VRF device. When an L3 interface is attached to a VRF, its local and connected routes are moved into the table associated with that VRF.
That behavior is extremely important.
Existing Routes Can Disappear
When an interface is moved into a VRF, do not assume every existing route associated with that interface survives unchanged.
The Linux kernel documentation specifically notes that while local and connected routes are moved into the VRF table, additional routes dependent on the interface can be dropped and may need to be reinserted afterward.
Therefore, after:
ip link set dev enp1s0 master vrf-blue
immediately inspect:
ip route show table 100
and:
ip route show table all
Never perform this operation remotely on your only management interface without a recovery path.
Step 5: Attach the Second Interface
Now:
sudo ip link set dev enp2s0 master vrf-red
Inspect:
ip link show master vrf-red
Our architecture is now:
enp1s0
↓
vrf-blue
↓
Table 100
enp2s0
↓
vrf-red
↓
Table 200
Step 6: Inspect the VRF Routing Tables
Use:
ip route show table 100
and:
ip route show table 200
iproute2 also supports:
ip route show vrf vrf-blue
and:
ip route show vrf vrf-red
The kernel VRF documentation explicitly documents both the VRF name and table-ID forms for displaying routes.
This makes troubleshooting much easier.
Step 7: Add the Default Gateway
For BLUE:
sudo ip route add table 100 default \
via 192.168.10.1 dev enp1s0
For RED:
sudo ip route add table 200 default \
via 10.20.30.1 dev enp2s0
Conceptually:
VRF BLUE
|
Table 100
|
default
|
192.168.10.1
while:
VRF RED
|
Table 200
|
default
|
10.20.30.1
Each routing domain now has its own default path.
Step 8: Understand the l3mdev Rule
Now run:
ip rule
You may see an l3mdev rule.
Modern Linux kernels use an l3mdev FIB rule to direct traffic into the routing table associated with the relevant Layer 3 master device.
The kernel documentation states that when the first VRF is created, Linux adds an l3mdev rule for IPv4 and IPv6 with a default preference of:
1000
A single rule can service all VRFs.
Conceptually:
Packet associated with VRF
↓
l3mdev rule
↓
VRF-associated routing table
↓
route
This is one of the most important pieces of the Linux VRF architecture.
Higher-Priority Policy Rules Can Still Win
VRF does not prevent you from using ordinary policy routing.
The kernel documentation explicitly notes that higher-priority policy-based routing rules can take precedence over VRF rules.
Therefore:
Custom PBR rule
priority 100
↓
checked before
↓
l3mdev rule
priority 1000
This makes Linux VRF highly flexible.
It also means a mysterious routing problem may actually be caused by an earlier ip rule.
Always inspect:
ip rule
when debugging VRF routing.
Step 9: Test VRF Route Lookups
Do not assume the configuration works.
Ask Linux.
For BLUE:
ip route get vrf vrf-blue 8.8.8.8
For RED:
ip route get vrf vrf-red 8.8.8.8
The kernel documentation explicitly supports:
ip route get vrf NAME ADDRESS
for VRF route testing.
This gives you direct evidence about the routing decision.
Why ip route get vrf Matters
Imagine:
ip route get vrf vrf-blue 8.8.8.8
returns a route through:
enp1s0
while:
ip route get vrf vrf-red 8.8.8.8
returns:
enp2s0
Now you have confirmed:
Same destination
↓
Different VRF
↓
Different routing table
↓
Different path
That is the core value of VRF.
Step 10: Run a Command Inside a VRF
One of the most useful iproute2 features is:
ip vrf exec
For example:
sudo ip vrf exec vrf-blue ping 8.8.8.8
Then:
sudo ip vrf exec vrf-red ping 8.8.8.8
You can also test HTTP:
sudo ip vrf exec vrf-blue curl https://example.com/
and:
sudo ip vrf exec vrf-red curl https://example.com/
The current ip-vrf(8) manual documents:
ip vrf exec NAME command...
and gives SSH executed inside a named VRF as an example.
Important ip vrf exec Requirement
There is an important technical detail.
The ip-vrf(8) manual states that the helper requires a kernel with:
CGROUPS
and:
CGROUP_BPF
enabled.
So if:
ip vrf exec ...
does not behave as expected, do not immediately conclude that the VRF routing table itself is broken.
Check platform/kernel support as part of the diagnosis.
VRF-Aware Applications
Applications can also bind sockets directly to a VRF device.
The kernel documentation describes using:
SO_BINDTODEVICE
to bind an application socket to the VRF. Packets through that socket then use the routing table associated with the VRF.
Conceptually:
Application
↓
Socket bound to vrf-blue
↓
Table 100
↓
enp1s0
This is particularly useful for:
- routing daemons;
- monitoring services;
- management tools;
- multi-tenant applications;
- network appliances.
Listening Services and VRF
Suppose SSH listens normally in the default routing domain.
Will it automatically accept traffic arriving from every VRF?
Not necessarily.
Linux provides settings including:
net.ipv4.tcp_l3mdev_accept
and:
net.ipv4.udp_l3mdev_accept
The kernel documentation states that these options are disabled by default, allowing socket selection to remain isolated by VRF context unless explicitly changed.
Inspect them:
sysctl net.ipv4.tcp_l3mdev_accept
and:
sysctl net.ipv4.udp_l3mdev_accept
Do not change these blindly.
First determine whether your application should:
listen inside one VRF
or:
serve multiple VRFs
Those are different architectures.
Capture Traffic Across an Entire VRF
One particularly useful feature is the ability to capture traffic using the VRF device.
For example:
sudo tcpdump -ni vrf-blue
The kernel documentation explains that packets appear to flow through the VRF device in the IPv4 and IPv6 processing stacks, allowing tcpdump on that device to capture traffic entering and leaving the VRF as a whole.
This is extremely convenient when a VRF contains several interfaces.
Instead of:
capture eth1
capture eth2
capture eth3
you may be able to observe the routing domain at:
vrf-blue
For more detailed packet analysis, use CorgiNetwork’s Wireshark Explained guide alongside the VRF capture.
VRF and Netfilter
VRF is not a firewall.
But firewall rules can interact with VRF traffic.
The Linux kernel documentation notes that netfilter rules can be applied using the VRF device to target traffic associated with the routing domain.
Therefore, when:
VRF route lookup works
but:
connection still fails
check the firewall.
For nftables:
sudo nft list ruleset
Do not assume that correct VRF routing guarantees application connectivity.
The packet still traverses other parts of the networking stack.
VRF and Reverse Path Filtering
Multi-homed routing can also interact with reverse-path validation.
If an incoming packet arrives through one routing domain while Linux believes its reverse path belongs somewhere else, source validation can become relevant.
Check:
sysctl -a 2>/dev/null | grep '\.rp_filter'
If packet captures show incoming traffic but services never receive it, continue with CorgiNetwork’s Linux rp_filter and Asymmetric Routing guide.
That article explains:
rp_filter = 0
rp_filter = 1
rp_filter = 2
and how reverse-route validation can affect multi-homed systems.
VRF Does Not Fix ARP Problems Automatically
Suppose multiple interfaces occupy the same Ethernet network.
You may still encounter unexpected neighbor or ARP behavior.
Inspect:
ip neigh
and:
ip neigh show
If the wrong interface appears to answer for an IPv4 address or neighboring hosts learn an unexpected MAC mapping, the problem belongs partly to ARP behavior rather than only routing.
Use CorgiNetwork’s Linux ARP Flux: Multiple NICs on the Same Subnet guide for that scenario.
Removing an Interface from a VRF
To remove:
enp1s0
from its VRF:
sudo ip link set dev enp1s0 nomaster
The kernel documentation states that connected routes move back to the default table and local entries return to the local table when the interface is removed from the VRF.
Immediately verify:
ip route
and:
ip route show table all
after removing an interface.
Removing a VRF
Before deleting a VRF, detach or otherwise correctly handle the member interfaces.
Then the VRF device can be removed with the normal link-management model.
For example:
sudo ip link delete vrf-blue
Do not perform this casually on a remote production machine.
Removing a routing domain can immediately change reachability.
Common Problem: VRF Exists but Has No Useful Route
You run:
ip vrf show
and see:
vrf-blue
but:
ip vrf exec vrf-blue ping 8.8.8.8
fails.
Inspect:
ip route show vrf vrf-blue
A VRF device existing does not guarantee that its routing table contains a usable default route.
Remember:
VRF
↓
Table
↓
Routes
All three pieces matter.
Common Problem: Interface Was Added but Connectivity Disappeared
You run:
ip link set enp1s0 master vrf-blue
and immediately lose connectivity.
One possible reason is that additional routes associated with that interface were not preserved when it moved into the VRF.
The kernel documentation explicitly warns that dependent routes beyond the automatically moved local/connected entries may need to be re-added to the VRF table.
Inspect:
ip route show table 100
before making additional changes.
Common Problem: Route Exists but the Application Still Fails
Suppose:
ip route get vrf vrf-blue DESTINATION
looks correct.
But the application cannot communicate.
Now check:
Application socket binding
↓
Firewall
↓
rp_filter
↓
Neighbor resolution
↓
Upstream gateway
VRF route correctness is only one layer.
Use:
ss -lntup
and:
sudo tcpdump -ni vrf-blue
to determine whether the problem is above or below the routing layer.
Common Problem: A Policy Rule Overrides VRF Behavior
Remember that higher-priority PBR rules can run before the l3mdev rule.
Inspect:
ip rule
Suppose you find:
100: from 10.20.30.20 lookup 500
1000: from all lookup [l3mdev-table]
The rule with priority:
100
is evaluated earlier.
Therefore, what appears to be a VRF failure may actually be an ordinary policy-routing rule taking precedence.
This is another reason to understand Linux Policy Routing before deploying complex VRFs.
Common Problem: Testing from the Default VRF
You configure:
vrf-blue
then run:
ping 8.8.8.8
and assume the command uses the BLUE routing table.
It may not.
Explicitly test:
sudo ip vrf exec vrf-blue ping 8.8.8.8
or perform a VRF-specific route lookup:
ip route get vrf vrf-blue 8.8.8.8
Test the routing domain you actually intend to validate.
Complete Linux VRF Troubleshooting Workflow
When VRF connectivity fails, use a consistent sequence.
1. List VRFs
ip vrf show
2. Inspect VRF devices
ip -d link show type vrf
3. Check member interfaces
ip link show master vrf-blue
4. Check addresses
ip -br addr
5. Inspect policy rules
ip rule
6. Inspect the VRF routing table
ip route show vrf vrf-blue
7. Inspect all routing tables
ip route show table all
8. Perform a route lookup
ip route get vrf vrf-blue DESTINATION
9. Test connectivity inside the VRF
sudo ip vrf exec vrf-blue ping DESTINATION
10. Capture packets
sudo tcpdump -ni vrf-blue
11. Check neighbors
ip neigh
12. Check firewall rules
sudo nft list ruleset
13. Inspect reverse-path filtering
sysctl -a 2>/dev/null | grep '\.rp_filter'
14. Check application sockets
ss -lntup
This sequence lets you move from:
VRF configuration
↓
routing
↓
actual packets
↓
application
instead of randomly changing network settings.
VRF Diagnostic Decision Tree
Connection fails
|
v
VRF exists?
/ \
NO YES
| |
Create v
VRF Interface enslaved?
/ \
NO YES
| |
Attach v
Route exists?
/ \
NO YES
| |
Fix table v
Route lookup correct?
/ \
NO YES
| |
Check rules v
Packet leaves?
/ \
NO YES
| |
Firewall / Check
rp_filter upstream
A Practical Production Design
Imagine a server used for both management and customer traffic.
Linux Server
/ \
MANAGEMENT PRODUCTION
VRF VRF
| |
Table 100 Table 200
| |
enp1s0 enp2s0
| |
Mgmt GW Prod GW
This is easier to reason about than a large collection of unrelated source rules.
An administrator can immediately ask:
Which VRF?
Which table?
Which interface?
Which gateway?
That organizational clarity is one of VRF’s biggest operational advantages.
VRF Is Not Automatically Security Isolation
This distinction is important.
A separate routing table is not equivalent to:
complete security sandbox
VRF primarily provides Layer 3 routing-domain separation.
Security still requires appropriate controls such as:
- nftables policies;
- application binding;
- service configuration;
- host hardening;
- upstream ACLs;
- appropriate namespace/container isolation where required.
Do not treat VRF as a replacement for a firewall.
VRF Is Also Not a Replacement for VLANs
Likewise:
VRF ≠ VLAN
A useful design might combine:
VLAN
↓
Layer 2 separation
↓
VRF
↓
Layer 3 routing separation
↓
Firewall
↓
Security policy
Each layer solves a different problem.
When Policy Routing May Be Simpler
Do not deploy VRF merely because it sounds more advanced.
If your only requirement is:
Source IP A → Gateway A
Source IP B → Gateway B
a few clean ip rule entries may be easier.
Use the Linux Policy Routing with ip rule and Multiple Routing Tables architecture first.
VRF becomes particularly attractive when the routing domains themselves need to be explicit operational objects.
When Network Namespaces May Be Better
If you need stronger separation involving:
interfaces
routing
sockets
network stack
process context
network namespaces may be a better architectural fit.
VRF and namespaces can also be combined.
The kernel documentation explicitly notes that VRF devices can exist inside network namespaces, allowing separation at several levels.
This gives Linux engineers a hierarchy such as:
Network Namespace
↓
VLAN
↓
VRF
↓
Routing Table
That architecture deserves careful design rather than automatic deployment.
Production Validation Checklist
Before considering a Linux VRF deployment complete, verify:
✓ VRF devices exist
✓ Every VRF uses the intended table ID
✓ Correct interfaces belong to each VRF
✓ Addresses remain correct
✓ Connected routes exist
✓ Default routes exist where required
✓ ip rule contains expected l3mdev behavior
✓ Higher-priority PBR rules are understood
✓ VRF route lookups return expected paths
✓ Gateways are reachable
✓ Applications use the intended VRF
✓ Firewall rules allow required traffic
✓ rp_filter behavior is understood
✓ ARP/neighbor resolution is correct
✓ Packet captures confirm real paths
✓ Configuration survives reboot
✓ Remote management remains reachable
✓ Recovery access exists
The most important test remains the actual packet path.
Frequently Asked Questions
What is Linux VRF?
Linux VRF provides Layer 3 routing-domain separation by associating a VRF device with a separate routing table and allowing interfaces to be attached to that device.
Is Linux VRF the same as a VLAN?
No. VLANs primarily provide Layer 2 segmentation, while VRFs provide Layer 3 routing separation.
Is Linux VRF the same as a network namespace?
No. A network namespace provides broader network-stack isolation. VRF focuses on Layer 3 routing domains, although VRFs can be used inside namespaces.
How do I create a Linux VRF?
A basic example is:
sudo ip link add vrf-blue type vrf table 100
sudo ip link set vrf-blue up
Then an interface can be attached:
sudo ip link set enp1s0 master vrf-blue
This follows the configuration model documented by the Linux kernel.
How do I list Linux VRFs?
Use:
ip vrf show
How do I display routes for a VRF?
Use:
ip route show vrf vrf-blue
or inspect its table directly:
ip route show table 100
Both forms are documented by the kernel VRF documentation.
How do I test a route inside a VRF?
Use:
ip route get vrf vrf-blue DESTINATION
For example:
ip route get vrf vrf-blue 8.8.8.8
How do I run a command through a VRF?
Use:
sudo ip vrf exec vrf-blue COMMAND
For example:
sudo ip vrf exec vrf-blue curl https://example.com/
The ip-vrf(8) manual documents ip vrf exec for executing commands against a named VRF.
Can I capture all traffic belonging to a VRF?
Linux’s VRF design allows tcpdump on the VRF device to observe traffic flowing through that VRF domain.
For example:
sudo tcpdump -ni vrf-blue
Does VRF use ip rule?
Yes. Modern Linux VRF uses an l3mdev FIB rule to direct lookups to the routing table associated with the Layer 3 master device.
Can normal policy-routing rules override a VRF rule?
Higher-priority policy-routing rules can take precedence over the VRF l3mdev rule.
Conclusion
Linux VRF provides a clean way to transform a complicated multi-interface machine from:
Many interfaces
↓
Many routing rules
↓
One difficult-to-understand network
into explicit routing domains:
VRF BLUE
↓
Table 100
↓
Gateway A
and:
VRF RED
↓
Table 200
↓
Gateway B
The essential commands are:
ip vrf show
ip -d link show type vrf
ip link set INTERFACE master VRF
ip route show vrf VRF
ip route get vrf VRF DESTINATION
ip vrf exec VRF COMMAND
ip rule
But VRF should not be viewed in isolation.
If you first need to understand how Linux selects routing tables, read CorgiNetwork’s Linux Policy Routing with ip rule and Multiple Routing Tables.
If packets use an unexpected NIC, continue with Linux Traffic Uses the Wrong Interface.
If reverse-path validation drops legitimate asymmetric traffic, use Linux rp_filter and Asymmetric Routing.
For multiple NICs on the same subnet and unexpected ARP responses, see Linux ARP Flux and Multiple NICs on the Same Subnet.
And for persistent Ubuntu multi-gateway configurations without VRF, use the Netplan Multiple Default Routes and Policy Routing guide.
Together, these articles build a progressively deeper CorgiNetwork learning path:
Wrong interface
↓
Multiple gateways
↓
ARP behavior
↓
Reverse-path filtering
↓
Policy routing
↓
Multiple routing tables
↓
VRF routing domains
That progression takes the reader from diagnosing a single wrong route to designing isolated Layer 3 routing domains on Linux.