Skip to content

How to add a persistent static route

Use this guide when a network can't be reached through the default gateway and needs its own route through another router. The example sends traffic for 203.0.113.0/24 through the gateway 192.0.2.1, which is on the same network segment as the interface <INTERFACE>.

Before you begin

  • <INTERFACE> stands for the name of the interface that reaches the gateway, such as enp0s4. List the interfaces and their addresses with ip -br addr show.
  • The interface already has a persistent address on the gateway's network, such as 192.0.2.10/24, configured with the distribution's default network tooling. The kernel only accepts a gateway that it can reach directly through a connected network.
  • You need sudo access.

Warning

A wrong route can cut off your own connection, for example if the destination you enter includes the address you're connecting from. Make changes from a console, or check that the destination doesn't cover the address of your SSH client before you apply them. To roll back a route you added with ip, delete it with ip route del (step 3); a route from the persistent configuration disappears when you remove it from the file or profile and apply the change.

Steps

  1. Add the route to the running kernel, to test it before you make it permanent:

    sudo ip route add 203.0.113.0/24 via 192.0.2.1 dev <INTERFACE>
    
  2. Check that the kernel would now send traffic for the network through the gateway:

    $ ip route show 203.0.113.0/24
    203.0.113.0/24 via 192.0.2.1 dev enp0s4
    $ ip route get 203.0.113.25
    203.0.113.25 via 192.0.2.1 dev enp0s4 src 192.0.2.10 uid 1000
        cache
    

    If hosts in 203.0.113.0/24 answer as you expect (for example with ping), the route is correct.

  3. Remove the test route, so that it doesn't conflict with the persistent one:

    sudo ip route del 203.0.113.0/24 via 192.0.2.1 dev <INTERFACE>
    

    A route added with ip route is lost at the next reboot in any case.

  4. Add the route to the persistent configuration of the interface:

    Which tool configures the interface depends on how the system was installed:

    • ifupdown on Debian systems installed with the Debian installer without a desktop: the interface has an iface <INTERFACE> stanza in /etc/network/interfaces or in a file in /etc/network/interfaces.d/.
    • netplan on Debian cloud images and on Ubuntu cloud and server installations: the interface is defined in a file in /etc/netplan/, and netplan hands the configuration to systemd-networkd.
    • NetworkManager on desktop installations: use the RHEL/Alma/Rocky instructions.

    If ifupdown configures the interface, show its stanza:

    $ grep -A2 "^iface <INTERFACE>" /etc/network/interfaces
    iface enp0s4 inet static
        address 192.0.2.10/24
    

    Add an up line with the ip route add command to the stanza, with a text editor or with this command:

    sudo sed -i "/^iface <INTERFACE> inet static/a\    up ip route add 203.0.113.0/24 via 192.0.2.1" /etc/network/interfaces
    

    ifupdown runs the up command each time it brings the interface up, after configuring the address. The stanza now looks like this:

    auto enp0s4
    iface enp0s4 inet static
        up ip route add 203.0.113.0/24 via 192.0.2.1
        address 192.0.2.10/24
    

    Warning

    Restarting an interface interrupts its connections. Don't restart the interface your SSH session uses unless you have a console.

    Restart the interface to apply the change:

    sudo ifdown <INTERFACE> && sudo ifup <INTERFACE>
    

    <!-- verify-setup

    Switch the lab to the netplan starting point for the next branch.

    ifdown "$LAB_NIC2" sed -i "/^auto $LAB_NIC2\$/,\$d" /etc/network/interfaces rm -f /etc/systemd/network/05-lab-unmanaged.network printf 'network:\n version: 2\n ethernets:\n %s:\n addresses:\n - 192.0.2.10/24\n' "$LAB_NIC2" > /etc/netplan/60-static.yaml chmod 600 /etc/netplan/60-static.yaml netplan apply for i in $(seq 30); do ip -4 addr show dev "$LAB_NIC2" | grep -q 'inet 192.0.2.10/24' && break; sleep 1; done ip -4 addr show dev "$LAB_NIC2" | grep -q 'inet 192.0.2.10/24' test -z "$(ip route show 203.0.113.0/24)" →

    If netplan configures the interface, find the netplan file that defines it:

    $ sudo grep -l <INTERFACE> /etc/netplan/*.yaml
    /etc/netplan/60-static.yaml
    

    Add the route to that file with netplan set. Pass the file name without .yaml as --origin-hint, so that the setting is written to the same file:

    sudo netplan set --origin-hint 60-static \
        "ethernets.<INTERFACE>.routes=[{to: 203.0.113.0/24, via: 192.0.2.1}]"
    

    The file now contains a routes list under the interface; you can also add the same lines with a text editor:

    $ sudo cat /etc/netplan/60-static.yaml
    network:
      version: 2
      ethernets:
        enp0s4:
          addresses:
          - "192.0.2.10/24"
          routes:
          - to: "203.0.113.0/24"
            via: "192.0.2.1"
    

    Apply the configuration:

    sudo netplan apply
    

    Edit only the files in /etc/netplan/. The 10-netplan-*.network files that networkctl status shows in /run/systemd/network/ are generated from them and overwritten by netplan apply and at boot.

    NetworkManager manages the network. Find the name of the connection profile that's active on <INTERFACE>:

    $ nmcli -g GENERAL.CONNECTION device show <INTERFACE>
    enp0s4
    

    Add the route to that profile, replacing <CONNECTION> with the profile name, and reactivate the profile to apply it:

    sudo nmcli connection modify "<CONNECTION>" +ipv4.routes "203.0.113.0/24 192.0.2.1"
    sudo nmcli connection up "<CONNECTION>"
    

    Check what the profile now contains:

    $ nmcli -g ipv4.routes connection show "<CONNECTION>"
    203.0.113.0/24 192.0.2.1
    

    The Arch Linux cloud image configures the network with systemd-networkd. Find the .network file that applies to <INTERFACE>:

    $ networkctl status <INTERFACE> | grep 'Network File'
            Network File: /etc/systemd/network/20-static.network
    

    Append a [Route] section to that file, replacing <NETWORK_FILE> with its path, and reload systemd-networkd:

    sudo tee -a <NETWORK_FILE> > /dev/null << EOF
    
    [Route]
    Destination=203.0.113.0/24
    Gateway=192.0.2.1
    EOF
    sudo networkctl reload
    

    If you manage the network with NetworkManager instead, use the RHEL/Alma/Rocky instructions.

    OpenRC with netifrc reads the network configuration from /etc/conf.d/net, where the interface's address is set in a config_<INTERFACE> line. Add a routes_<INTERFACE> line for the route:

    sudo tee -a /etc/conf.d/net > /dev/null << EOF
    routes_<INTERFACE>="203.0.113.0/24 via 192.0.2.1"
    EOF
    

    In these variable names, any character of the interface name other than a letter, digit or underscore is replaced by an underscore. The file now contains both lines:

    config_enp0s4="192.0.2.10/24"
    routes_enp0s4="203.0.113.0/24 via 192.0.2.1"
    

    Restart the interface's service to apply the change; its output includes an Adding routes line for the new route:

    sudo rc-service net.<INTERFACE> restart
    

    On the systemd profile, systemd-networkd configures the network: follow the Arch instructions (systemd profile, not verified).

  5. Check that the route is back, this time from the persistent configuration:

    $ ip route show 203.0.113.0/24
    203.0.113.0/24 via 192.0.2.1 dev enp0s4 proto static
    

    The details after the device name vary with the tool: NetworkManager shows proto static metric 101, and netifrc shows only a metric, such as metric 3.

Verify

Restart the system at a convenient time, to make sure the route is restored at boot:

sudo reboot

After the restart, the route is present without any manual step:

$ ip route show 203.0.113.0/24
203.0.113.0/24 via 192.0.2.1 dev enp0s4 proto static

Troubleshooting

Error: Nexthop has invalid gateway.
The gateway isn't on any network the system is directly connected to. Check that <INTERFACE> is up and has an address on the gateway's network with ip -br addr show dev <INTERFACE>.
RTNETLINK answers: File exists
A route to the same destination already exists, for example from an earlier test. Show it with ip route show 203.0.113.0/24 and delete it with ip route del before you add it again.
The route is missing after a reboot
The interface's persistent configuration wasn't applied, often because the route was added to a different file or profile than the one that's active on the interface. Check which configuration applies to the interface: sudo grep -l <INTERFACE> /etc/netplan/*.yaml (netplan; the 10-netplan-* files in /run/systemd/network/ are generated and must not be edited), the iface <INTERFACE> stanza in /etc/network/interfaces or /etc/network/interfaces.d/ (ifupdown), networkctl status <INTERFACE> (systemd-networkd), nmcli device show <INTERFACE> (NetworkManager), or /etc/conf.d/net (netifrc).