Skip to main content
RunBook Academy

LinuxXXI · Advanced Linux NetworkingRouting tables

Multiple routing tables on Linux

Advanced⏱ ~12 minip

What you'll learn

  • Explain why multiple routing tables exist
  • Read and modify ip rule and ip route show table
  • Add a custom routing table and route traffic into it
  • Recognise when multiple tables are the right tool

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09

Not yet marked complete on this device.

A Linux host can have many routing tables, not just the “main” one. Each table holds routes; the kernel picks a table for each packet using rules. This is the foundation of policy routing - choosing routes based on source address, mark, interface, or other criteria.

The default tables

TableNumberContents
local255Routes for local and broadcast addresses
main254The “main” routing table (the default)
default253Empty by default; used as a fallback
ip route show table local
ip route show table main
ip route show table all    # every table

The local table holds entries for 127.0.0.0/8, broadcast addresses, and anycast addresses. The kernel creates these automatically and you should not modify them.

The main table is where most user configuration goes.

Custom tables

Add a new table by name and number:

echo "100 mgmt" >> /etc/iproute2/rt_tables

Then add routes to it:

ip route add 10.0.0.0/24 dev eth0 table mgmt
ip route add default via 10.0.0.1 dev eth0 table mgmt

Tables are isolated: a route in table mgmt is not visible to traffic in table main. The kernel only looks at table mgmt when a rule sends traffic there.

ip rule - selecting a table

ip rule show

Output:

0:    from all lookup local
32766:    from all lookup main
32767:    from all lookup default

Each rule has:

  • Priority: lower is consulted first.
  • Selector: from, to, iif, oif, mark, etc.
  • action: lookup <table>, blackhole, prohibit.

The default rules consult local, then main, then default. To send traffic from a specific source to a specific table, add a rule with higher priority:

ip rule add from 10.0.0.0/24 lookup mgmt priority 100
ip rule show

Now traffic from 10.0.0.0/24 first consults table mgmt. If table mgmt has no route for the destination, the next rule applies (eventually table main).

A worked example

A host has two interfaces: eth0 (production, 10.0.0.0/24) and eth1 (backup, 192.168.1.0/24). You want:

  • Traffic from 10.0.0.0/24 uses eth0.
  • Traffic from 192.168.1.0/24 uses eth1.
  • Traffic from any other source uses eth0’s default route.
echo "100 backup" >> /etc/iproute2/rt_tables
ip route add 192.168.1.0/24 dev eth1 table backup src 192.168.1.10
ip route add default via 192.168.1.1 dev eth1 table backup
ip rule add from 192.168.1.0/24 lookup backup priority 100

Verify:

ip route get 8.8.8.8 from 10.0.0.10
ip route get 8.8.8.8 from 192.168.1.10

The kernel should pick different routes for the two source addresses.

Persistent configuration

The /etc/iproute2/rt_tables file makes table names stable. Rules and routes in custom tables are made persistent via the network manager:

With Netplan:

network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      addresses: [10.0.0.10/24]
      routes:
        - to: default
          via: 10.0.0.1
    eth1:
      addresses: [192.168.1.10/24]
      routes:
        - to: default
          via: 192.168.1.1
          table: 100
      routing-policy:
        - from: 192.168.1.0/24
          table: 100
          priority: 100

Netplan has no top-level routing: key, and no policy: or tables: children. Both the routes and the rules belong to the interface they apply to: routes: entries take an optional table:, and routing-policy: holds the ip rule equivalents. Netplan wants the table number, not a name from /etc/iproute2/rt_tables - that file names tables for the ip command only, and systemd-networkd never reads it.

The snippet above renders to exactly the systemd-networkd units you would write by hand:

# /run/systemd/network/10-netplan-eth1.network
[Route]
Destination=0.0.0.0/0
Gateway=192.168.1.1
Table=100

[RoutingPolicyRule]
From=192.168.1.0/24
Table=100
Priority=100

With systemd-networkd directly, the same configuration is a [Route] section with Table= and a [RoutingPolicyRule] section, as shown above. Hand-written ip commands in a boot service are a third option, but they are invisible to the network manager and are lost on the next netplan apply.

When to use multiple tables

  • Two uplinks, different source ranges (like the example above).
  • VRF-like isolation for sensitive workloads.
  • Tenant isolation in a multi-tenant host (when namespaces are overkill).
  • Routing in containers (CNI uses tables internally).

For most single-purpose hosts, the main table is enough.

Knowledge check

Knowledge check · 5 questions

  1. Q1. What command lists all routing tables?

  2. Q2. Rules are consulted in priority order - lower priority numbers first.

  3. Q3. Which of the following are real Linux routing tables? Select all that apply.

  4. Q4. Where does a policy routing rule belong in a Netplan file?

  5. Q5. One unrecognised key anywhere in a Netplan file makes netplan apply abort without applying any part of it.

Passing score: 75%. Answers are checked in this browser.