Linux VRF Explained: Isolated Routing Tables with iproute2

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.

Leave a Reply

Your email address will not be published. Required fields are marked *