A server whose IP address changes is a server you eventually cannot find. DHCP is perfect for laptops and phones, but anything that other machines connect to (a web server, a database, a NAS, a Kubernetes node) needs an address that survives reboots and lease renewals. That means configuring a static IP.
On Ubuntu, network configuration goes through Netplan, and this tutorial shows the whole process on Ubuntu 20.04, 22.04, and 24.04: convert an interface from DHCP to a static address with the current routes syntax (the old gateway4 directive is deprecated), handle the cloud-init trap that silently reverts your changes, apply the configuration without locking yourself out of a remote machine, and verify address, gateway, and DNS all work.
DHCP vs Static: What Actually Changes
With DHCP, the interface asks a server on the network for its configuration and receives four things on a lease: an IP address, a subnet mask, a default gateway, and DNS servers. The lease expires and renews, and nothing guarantees the same address next time (unless the DHCP server reserves it).
With a static configuration, you write those same four things into a file yourself:
- Address + prefix, e.g.
192.168.1.50/24. The/24is the subnet mask in CIDR form (255.255.255.0); get it wrong and machines on your own network become unreachable. - Default gateway, the router that handles traffic leaving your subnet, e.g.
192.168.1.1. - DNS servers, who resolves names for you, e.g. your router or public resolvers like
8.8.8.8.
One rule before you pick an address: it must be outside the DHCP server’s pool (or reserved in it), otherwise the DHCP server may hand the same address to another machine and you get an IP conflict with confusingly intermittent symptoms.
Prerequisites
- Ubuntu 20.04, 22.04, or 24.04 LTS (server or desktop; on desktop, NetworkManager may own the interfaces, and this guide covers the Netplan/networkd setup used on servers)
- A user with
sudoprivileges - A free IP address on your subnet, plus your gateway and DNS server addresses
- Console access as a fallback (physical, VNC, KVM, or cloud console) in case a remote change goes wrong
Step 1: Find Your Interface Name and Current Configuration
List the interfaces:
ip link show
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
Modern Ubuntu uses predictable names like ens18, enp0s3, or eno1 instead of eth0. Note yours and substitute it throughout.
Check what address the interface currently has (from DHCP):
ip addr show ens18
ip route | grep default
The current address, prefix length, and gateway are exactly the values you will want to reuse if your goal is “keep this address, but make it permanent”.
Step 2: Find the Netplan Configuration File
ls /etc/netplan/
You will see one or more YAML files, commonly 00-installer-config.yaml (Ubuntu Server installer) or 50-cloud-init.yaml (cloud images, Raspberry Pi, VPS providers). Netplan merges all files in lexicographic order, later files overriding earlier ones.
The cloud-init caveat
If your file is 50-cloud-init.yaml, the machine was provisioned by cloud-init, and on some setups cloud-init regenerates that file on every boot, silently reverting your edits. Check the file’s header comment; if it warns about changes not persisting, disable cloud-init’s network management first:
sudo nano /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
network: {config: disabled}
After that, cloud-init leaves /etc/netplan/ alone and your static configuration survives reboots. (On most VPS providers, also check their docs, since some manage addresses from the hypervisor side.)
Back up whichever file you are about to edit:
sudo cp /etc/netplan/50-cloud-init.yaml /etc/netplan/50-cloud-init.yaml.bak
Step 3: Write the Static IP Configuration
Open the file:
sudo nano /etc/netplan/50-cloud-init.yaml
A typical DHCP configuration looks like this:
network:
version: 2
ethernets:
ens18:
dhcp4: true
Replace it with the static configuration:
network:
version: 2
ethernets:
ens18:
dhcp4: false
addresses:
- 192.168.1.50/24
routes:
- to: default
via: 192.168.1.1
nameservers:
addresses:
- 192.168.1.1
- 8.8.8.8
Line by line:
dhcp4: falsestops asking the DHCP server; this interface’s configuration is now fully manual.addressesis the static IP in CIDR notation. The prefix (/24) is mandatory;192.168.1.50alone is a syntax error.routeswithto: defaultsets the default gateway. This is the current syntax on all supported Ubuntu versions. If you have an existing config (or an old tutorial) usinggateway4:, replace it. It has been deprecated since Ubuntu 22.04 and misbehaves on 24.04:
# Old (deprecated, do not use):
gateway4: 192.168.1.1
# Current:
routes:
- to: default
via: 192.168.1.1
nameservers.addresseslists DNS resolvers, tried in order. Using your router first and a public resolver second is a sensible default; replace with your organization’s DNS if you have one.
YAML formatting rules apply strictly: spaces only, no tabs, consistent indentation. Most Netplan errors are indentation errors.
Step 4: Apply the Configuration Safely
If you are connected over SSH, do not use netplan apply directly. If the new address, gateway, or DNS is wrong, you lose the session and possibly the machine. Use the built-in safety net:
sudo netplan try
netplan try applies the configuration and 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
One subtlety when changing the machine’s address over SSH: your session rides on the old address, so it will freeze the moment the new config applies. Reconnect to the new address in a second terminal. If that works, you know the config is good. If you cannot get back in, do nothing: after 120 seconds the old configuration returns on its own.
With physical or console access, the direct command is fine:
sudo netplan apply
Step 5: Verify Address, Gateway, and DNS
Three checks, one per configured item. First, the address:
ip addr show ens18
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
inet 192.168.1.50/24 brd 192.168.1.255 scope global ens18
Second, the default route, and that traffic actually leaves the subnet:
ip route | grep default
ping -c 3 8.8.8.8
default via 192.168.1.1 dev ens18 proto static
Third, DNS resolution:
resolvectl status ens18
ping -c 3 google.com
resolvectl status shows which DNS servers the interface is actually using. If ping 8.8.8.8 works but ping google.com does not, your problem is DNS, not routing. Recheck the nameservers block.
On Ubuntu 22.04 and later, sudo netplan status summarizes all of it (addresses, routes, and DNS per interface) in one command.
Common Problems and Troubleshooting
The configuration reverts after a reboot.
Cloud-init regenerated the file. Apply the fix from Step 2 (99-disable-network-config.cfg) and re-add your configuration.
netplan try fails instantly with a parse error.
YAML indentation. Tabs are forbidden, and every nesting level must be consistent. Compare against the example above character by character. The error message includes the line number.
The address is configured but neighbours cannot reach it (or you get intermittent conflicts).
Another machine may hold the same address, likely handed out by the DHCP server. Check from another host with arping 192.168.1.50 (two different MAC addresses answering is the smoking gun). Move to an address outside the DHCP pool or add a reservation.
Internet works by IP but names do not resolve.
DNS configuration problem. resolvectl status ens18 shows what the system is really using; if your nameservers block is missing or mis-indented, the interface may have no resolvers at all after leaving DHCP.
You locked yourself out of a remote server.
Console access is the only way back: restore the .bak file from Step 2 and run netplan apply from the console. Next time, netplan try.
A second interface also has a default route.
Only one interface should carry to: default. Two default routes cause traffic to leave through the wrong interface unpredictably. Give secondary interfaces specific routes instead, as covered in How to Add Static Route Using Netplan on Ubuntu.
Best Practices
Use netplan try on anything remote. It is the single habit that prevents locked-out servers. The 120-second auto-revert costs nothing.
Document the address, or better, reserve it. A static IP that lives only in one server’s YAML is how you end up creating an IP conflict six months later. Keep a small inventory, and put reservations in the DHCP server for addresses you assign statically.
Back up before editing, and restrict permissions after:
sudo cp /etc/netplan/50-cloud-init.yaml /etc/netplan/50-cloud-init.yaml.bak
sudo chmod 600 /etc/netplan/50-cloud-init.yaml
Netplan warns when its files are world-readable; 600 silences the warning and keeps your network layout private from other users on the system.
One file, unless you have a reason. Netplan merges every YAML in /etc/netplan/ in name order, which is powerful and confusing. For a single server, keep all interface config in one file.
Conclusion
Your Ubuntu server now has a permanent address: DHCP is off, the static address, gateway (via the modern routes syntax), and DNS are pinned in Netplan, cloud-init can no longer undo your work, and you verified each layer (link, route, resolution) independently.
Static addressing is the foundation for most other server networking. From here: reach other subnets through specific gateways with How to Add Static Route Using Netplan on Ubuntu, put one interface in several networks at once with How to Configure a VLAN Trunk Interface with Netplan on Ubuntu, and bond two NICs for redundancy with How to Set Up LACP Bonding Interfaces on Ubuntu.