How to Add Static Route Using Netplan on Ubuntu

Written by: Bagus Facsi Aginsa
Published at: 16 May 2026


Your main interface connects to the internet through your default gateway. But what happens when you have a second interface connected to a private network, and the servers on that network sit behind a different router? Without an explicit routing rule, traffic destined for that private network either gets misrouted through the default gateway or dropped entirely.

A static route tells the kernel exactly how to reach a specific destination: send this traffic via that gateway on this interface. This tutorial shows you how to add a persistent static route using Netplan on Ubuntu 20.04, 22.04, and 24.04. You will use the current routes syntax, the old gateway4 directive is deprecated, apply the change safely so you cannot lock yourself out of a remote server, and verify the route is actually working.


Default Gateway vs Static Route

Before touching any configuration, it helps to know when you need a static route and when you do not.

A default gateway is the router that handles all traffic with no more specific destination. When your machine needs to reach a public IP it has never seen before, it forwards the packet to the default gateway and lets the router figure out the path. You configure a default gateway once, on the interface that connects to the internet.

A static route is more specific. It says: traffic going to this destination should go via this gateway. You add a static route when your machine has multiple network interfaces and some traffic needs to take a different path from the default. Without it, everything flows through the single default gateway, including traffic that should stay on the internal network.

The classic scenario: one interface connects to the public internet, another connects to a private subnet behind its own router. The private subnet is unreachable through the default gateway; it needs its own explicit route.


Prerequisites

  • Ubuntu 20.04, 22.04, or 24.04 LTS
  • A user with sudo privileges
  • Netplan installed, it ships by default on all modern Ubuntu versions
  • At least two network interfaces, or multiple subnets reachable through different gateways
  • Basic familiarity with the Linux command line

The Use Case

To keep the examples concrete, this tutorial uses the following network setup:

                  ___                        _________
                 |   |                      |         |
  internet ------| A |--- 192.168.1.0/24 ---| server1 |
                 |___|                      |_________|
                 router                          |
                                                 |
                                           10.1.1.128/26
                                                 |
                     _________                  _|_
                    |         |                |   |
                    | server2 |----------------| B |
                    |_________|                |___|
                                               router
  • server1 is your machine. It has two network interfaces.
  • ens18 has the IP 192.168.1.100/24 and connects to the internet through router A (192.168.1.1).
  • ens19 has the IP 10.1.1.130/26 and connects to the private network through router B (10.1.1.129).
  • server2 has the IP 10.1.2.100 and sits behind router B, reachable only via the 10.1.2.0/24 subnet.

To reach server2 from server1, you need a static route: traffic going to 10.1.2.100, or anywhere in 10.1.2.0/24, should be sent via 10.1.1.129 on ens19.


Step 1: Find Your Interface Names

Modern Ubuntu does not use eth0 and eth1. Predictable network interface names depend on your hardware and hypervisor. Common names are ens18, ens3, enp0s3, or enp1s0.

List your interfaces:

ip link show

Example output:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP
    link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
3: ens19: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP
    link/ether 52:54:00:ab:cd:ef brd ff:ff:ff:ff:ff:ff

Your names will likely differ. Substitute the real names throughout this tutorial wherever you see ens18 and ens19.


Step 2: Open the Netplan Configuration File

Netplan configuration files live in /etc/netplan/. List what is there:

ls /etc/netplan/

You will typically see one YAML file, often named 00-installer-config.yaml, 50-cloud-init.yaml, or similar. Open it with your preferred editor:

sudo nano /etc/netplan/50-cloud-init.yaml

Step 3: Write the Static Route Configuration

Here is the complete configuration for the use case above, using the current Netplan syntax:

network:
  version: 2
  ethernets:
    ens18:
      addresses:
        - 192.168.1.100/24
      routes:
        - to: default
          via: 192.168.1.1
      nameservers:
        addresses:
          - 8.8.8.8
    ens19:
      addresses:
        - 10.1.1.130/26
      routes:
        - to: 10.1.2.100
          via: 10.1.1.129

A note on gateway4

If you are working from an older tutorial or an existing config file that uses gateway4, replace it. The gateway4 directive has been deprecated since Ubuntu 22.04 and does not work correctly on Ubuntu 24.04. When Netplan encounters it, it prints a deprecation warning and may behave unexpectedly. The modern equivalent is a routes entry with to: default:

# Old (deprecated, do not use on Ubuntu 22.04 or later):
gateway4: 192.168.1.1

# Current syntax works on Ubuntu 20.04, 22.04, and 24.04:
routes:
  - to: default
    via: 192.168.1.1

The to: default shortcut is equivalent to to: 0.0.0.0/0 and works in all current Netplan versions.

On an older, un-updated 20.04, the to: default may not be recognised (you’d need to: 0.0.0.0/0)

The static route on ens19

The routes block under ens19 adds a specific entry to the kernel’s routing table: send traffic going to 10.1.2.100 via the gateway 10.1.1.129, using the ens19 interface. All other traffic follows the default route defined on ens18.

Why no default route on ens19?

A machine should only have one default gateway. Defining to: default on both interfaces creates an ambiguous routing table and causes unpredictable behaviour depending on which route has a lower metric. Only the interface that connects to the internet gets a default route. All other interfaces get specific routes only.

Routing to a whole subnet

The example above routes only to the single host 10.1.2.100. In practice, you often need to reach any machine in the 10.1.2.0/24 subnet. Use a subnet route instead:

ens19:
  addresses:
    - 10.1.1.130/26
  routes:
    - to: 10.1.2.0/24
      via: 10.1.1.129

This single route covers all 254 usable addresses in the 10.1.2.0/24 range. Use a subnet route when you have multiple servers behind router B; use a host route (to: 10.1.2.100) only when you need to reach one specific IP.


Step 4: Apply the Configuration

The safe way: netplan try

On any remote server, use netplan try instead of netplan apply:

sudo netplan try

netplan try applies the new configuration immediately, then starts a 120-second countdown:

Do you want to keep these settings?

Press ENTER before the timeout to accept the new configuration.

Changes will revert in 115 seconds

If the new config works and you can still reach the server, press Enter to confirm. If something goes wrong and you lose connectivity, the configuration reverts automatically after 120 seconds. This is the mechanism that saves you from being permanently locked out of a remote machine after a bad network change.

The direct way: netplan apply

If you have physical or console access (you are at the machine, or using a VNC or KVM console), apply without the revert countdown:

sudo netplan apply

If Netplan finds an error, it reports the file and line number. YAML is whitespace-sensitive, so the most common error is incorrect indentation, more on that in the troubleshooting section below.


Step 5: Verify the Route

Check the routing table to confirm the new route is present:

ip route list

Expected output:

default via 192.168.1.1 dev ens18 proto static
192.168.1.0/24 dev ens18 proto kernel scope link src 192.168.1.100
10.1.1.128/26 dev ens19 proto kernel scope link src 10.1.1.130
10.1.2.100 via 10.1.1.129 dev ens19 proto static

You can see four entries: the default route out through router A, the two directly connected networks, and your new static route to 10.1.2.100.

To check exactly which route the kernel would use for a specific destination:

ip route get 10.1.2.100
10.1.2.100 via 10.1.1.129 dev ens19 src 10.1.1.130 uid 0
    cache

This confirms the kernel will send traffic to 10.1.2.100 via 10.1.1.129 on ens19. If this shows the wrong interface or gateway, the configuration has a problem.

On Ubuntu 22.04 and later, you can also inspect the live network state with:

sudo netplan status

This shows addresses, routes, and DNS resolvers for each interface as Netplan currently understands them.


Temporary Static Routes

If you want to test a route without editing the Netplan config, add it immediately with ip route add:

sudo ip route add 10.1.2.100 via 10.1.1.129 dev ens19

The route takes effect instantly. It does not survive a reboot, the kernel’s routing table is reset on restart. Once you have confirmed the route works as expected, make it permanent by adding it to the Netplan configuration and applying it.

To remove a temporary route before rebooting:

sudo ip route del 10.1.2.100 via 10.1.1.129 dev ens19

IPv6 Static Routes

Netplan uses the same routes syntax for IPv6. To add a static route to an IPv6 subnet, add it to the routes block alongside any IPv4 routes:

ens19:
  addresses:
    - 10.1.1.130/26
  routes:
    - to: 10.1.2.0/24
      via: 10.1.1.129
    - to: "2001:db8:1::/48"
      via: "2001:db8::1"

For an IPv6 default route:

routes:
  - to: default
    via: "2001:db8::1"

The to: default shortcut works for both IPv4 and IPv6 in all current Netplan versions.


Common Mistakes and Troubleshooting

YAML indentation errors.

Netplan configuration is whitespace-sensitive. Tabs are not allowed, use spaces only. Each nested level must be consistently indented (2 or 4 spaces are both fine, but must be consistent throughout the file). If netplan try fails immediately with a parse error, incorrect indentation is the most likely cause. Compare your file against the example above, checking every line.

Wrong interface name.

If the route does not appear in ip route list after applying, verify the interface names in your config exactly match the real names shown by ip link show. A single typo causes Netplan to silently skip the entire configuration block for that interface.

You applied a bad config and lost SSH access.

If you used netplan apply and your SSH session dropped, you need console access from VNC, KVM, or your cloud provider’s recovery console to fix it. The only way to prevent this is to use netplan try before you lose access, so the auto-revert can save you. If you are now locked out, restore the backup you made before editing (you did back up, right?) and apply it from the console.

Permission warning: “Permissions for /etc/netplan/… are too open”.

Netplan prints this warning when the config file is readable by users other than root. Fix it:

sudo chmod 600 /etc/netplan/50-cloud-init.yaml

The warning does not prevent the config from applying, but tightening permissions means other users on the system cannot read your network configuration.

Route already exists.

If ip route add reports RTNETLINK answers: File exists, a route to that destination already exists in the routing table. Check what is there:

ip route show 10.1.2.0/24

If the existing route already goes via the correct gateway, no action is needed. If it is wrong, delete it first:

sudo ip route del 10.1.2.0/24

Multiple default gateways.

If you accidentally added to: default on two different interfaces, some traffic will take the wrong path. Check for duplicate default routes:

ip route | grep default

There should be exactly one line. If there are two, remove the to: default entry from the interface that should not have it, then reapply the Netplan config.


Best Practices

Always use netplan try on remote machines. It is the single habit that prevents the most painful outcome in network configuration, being permanently locked out of a server over SSH. The automatic 120-second revert is a safety net that costs nothing but a couple extra seconds.

Never define more than one default gateway. Define to: default on exactly one interface: the one connected to the internet. Put specific routes entries on all other interfaces. Two default gateways is almost always a mistake.

Back up before editing. Copy the current config to a safe location before you change anything:

sudo cp /etc/netplan/50-cloud-init.yaml /etc/netplan/50-cloud-init.yaml.bak

If you make a mistake that prevents the system from booting into a usable network state, you can restore this backup from the recovery console.

Config file ordering. Netplan reads all YAML files in /etc/netplan/ in lexicographic order. Files with lower numbers are processed first. If you create a second file for custom routes, name it with a higher number (like 99-custom-routes.yaml) so it is processed after the base configuration. For most single-server setups, one file is sufficient.

Restrict config file permissions. Always set Netplan config files to 600 after editing:

sudo chmod 600 /etc/netplan/50-cloud-init.yaml

Conclusion

You have added a persistent static route using Netplan on Ubuntu, with the current routes syntax rather than the deprecated gateway4. You applied the change safely with netplan try, verified the result with ip route list and ip route get, and covered the common failure modes.

From here, the next steps depend on your setup. If you need secure encrypted connectivity between servers across different networks, a VPN removes the need for manually maintained static routes and encrypts traffic in transit: How to Set Up a WireGuard VPN Server on Ubuntu covers the setup end to end, and How to Connect Remote Servers with ZeroTier on Ubuntu is an alternative that works without any port forwarding. If you are working with multiple physical links and need interface redundancy, see How to Set Up LACP Bonding Interfaces on Ubuntu.