Netplan Crash Course for Ubuntu Dedicated Servers
Netplan is the YAML layer that configures networking on Ubuntu Server: you describe addresses, routes and DNS in one file, and a generator renders that into config for systemd-networkd. This crash course is written for a dedicated box with a static public IP, no Wi-Fi, and SSH as the only way in. It covers where the files live, the root-only 600 permission rule, a full static IPv4 and IPv6 example, the generate, try and apply commands that stop you locking yourself out, plus VLANs, bonds, bridges, and the cloud-init file that keeps overwriting people's edits.
Loading...
What Netplan is, and whether you're using it
Netplan is the network configuration layer Ubuntu has shipped by default since 17.10. You write your interfaces, addresses, routes and DNS as YAML in /etc/netplan/, and a generator turns that single description into configuration for whichever backend drives the network underneath: systemd-networkd on Ubuntu Server, NetworkManager on Ubuntu Desktop. You edit one file format; Netplan renders the backend-specific config below it.
If you rented a dedicated server and installed Ubuntu on it, you have Netplan. This guide is written for that exact machine: one box, a static public IPv4 address (and often an IPv6 allocation), no Wi-Fi, and SSH as the only way in. That last detail sets the tone throughout, because a networking mistake on a remote server is how you lock yourself out of it. Netplan has a specific tool to stop that happening, and we lean on it every time we change something live.
The crash course covers:
- where the configuration files live, and the permission rule that trips people up
- the two-backend model, and what changes when you copy config off a tutorial written for the other one
- a full static IPv4 and IPv6 configuration you can adapt line by line
- the commands you run day to day:
generate,try,applyandstatus - VLANs, LACP bonds, and a bridge for VM hosts
- why your edits sometimes vanish on reboot, and the cloud-init setting that fixes it
A quick version note. Ubuntu 26.04 LTS "Resolute Raccoon", released on 23 April 2026, ships the Netplan 1.2 series. The previous LTS, 24.04 "Noble Numbat", shipped Netplan 1.0, the first stable release, dated 29 February 2024. The upstream series is 1.2.x at the time of writing; check the releases page for the current version. Everything here applies to both, with the differences called out where they occur. If you are still choosing which release to run, the Ubuntu Server LTS guide covers the support timelines.
Where the configuration lives
Every *.yaml file in /etc/netplan/ counts. Two other directories feed in as well: /run/netplan/ for runtime configuration and /lib/netplan/ for vendor defaults. Files are read in lexicographical order by name and merged into one model. When the same key is set twice, the later file wins. If a file of the same name exists in more than one directory, the copy in /run/netplan/ takes precedence over /etc/netplan/, which takes precedence over /lib/netplan/.
This ordering is why a file called 50-cloud-init.yaml matters. On most cloud and dedicated-server images, cloud-init writes your initial network settings to exactly that file, and its 50- prefix sits early in the sort order. Drop your own 90-static.yaml next to it and yours wins the merge, but cloud-init may rewrite its own file on the next boot and undo the assumptions you made. The clean fix is further down, in troubleshooting. For now, know that 50-cloud-init.yaml is generated, not hand-written, and treat it as such.
One rule catches almost everyone at least once. Every file under /etc/netplan/ should be owned by root and readable only by root, mode 600. Netplan stores credentials such as Wi-Fi passwords and tunnel keys in these files, so a world-readable config leaks secrets. Since version 0.106 the tool prints a warning when it finds a file that anyone but root can read:
** (generate:1234): WARNING **: Permissions for /etc/netplan/50-cloud-init.yaml
are too open. Netplan configuration should NOT be accessible by others.
Fix it in one command:
chmod 600 /etc/netplan/*.yaml
That warning is easy to ignore because the network keeps working, but tightening the permissions is part of any server hardening pass and costs nothing.
One YAML source, two backends
Netplan itself does not manage a single interface. It generates configuration for a renderer, and the renderer does the work. There are two, and which one runs by default depends on how Ubuntu was installed:
| System | Default renderer | Typical use |
|---|---|---|
| Ubuntu Server | systemd-networkd | Dedicated servers, cloud images, minimal installs |
| Ubuntu Desktop | NetworkManager | Workstations and laptops with a GUI |
On a server you rarely change this. systemd-networkd starts early in boot, has no GUI dependencies, and is the right backend for a machine you reach over SSH. You can name it explicitly with renderer: networkd at the top of the file, though on Ubuntu Server it is already the default.
The reason the distinction matters: configuration you copy from a desktop-oriented tutorial may set renderer: NetworkManager or rely on NetworkManager-only keys, and it will behave differently, or refuse to apply, on a networkd server. When you borrow a snippet, check which backend it assumes before you paste it.
A complete static configuration
Here is a full static setup for a single interface with both IPv4 and IPv6. It assigns the addresses, sets the default routes, and configures DNS. Interface names on modern Ubuntu follow the predictable scheme, so yours is likely enp1s0, eno1, or similar; run ip -br link to see what your kernel calls it.
# /etc/netplan/90-static.yaml
network:
version: 2
renderer: networkd
ethernets:
enp1s0:
dhcp4: false
dhcp6: false
addresses:
- 192.0.2.10/24
- "2001:db8::10/64"
routes:
- to: default
via: 192.0.2.1
- to: default
via: "2001:db8::1"
nameservers:
addresses:
- 192.0.2.53
- 2001:db8::53
search:
- example.com
version: 2 is the Netplan format version and is required. dhcp4: false is already the default once you supply static addresses, but stating it makes your intent plain to the next person. Each address carries its prefix length in CIDR form. The routes block is the part worth reading twice.
You will find older tutorials that set the gateway like this:
gateway4: 192.0.2.1
That key is deprecated. Netplan has warned against gateway4 and gateway6 since version 0.103, and the message is direct:
gateway4 has been deprecated, use default routes instead.
The replacement is the routes entry with to: default, exactly as in the full example above. It does the same job, works for IPv4 and IPv6 through one mechanism, and lets you add non-default routes later without changing style. Use it, and skip gateway4 even though it still functions.
Please do not reuse the exact IPs and interface names shown here. The addresses come from the TEST-NET and documentation ranges reserved for examples; substitute the values your provider gave you.
The command set: generate, try, apply
Netplan does nothing to your live network until you run one of three commands. Knowing the difference between them is what keeps a remote session alive.
netplan generate renders your YAML into backend configuration under /run/systemd/network/ (for networkd) without touching the running network. It doubles as a validator: if your YAML has a syntax error or an unknown key, generate fails and tells you where, and nothing has changed yet. Run it first whenever you are unsure.
The command that changes the live system is netplan apply. It regenerates the config and then applies it at once, reconfiguring the interface in place on a static-address change. On a remote box the danger is immediate: if the new config is wrong, you have just cut your own SSH session, and there is no undo from where you are sitting.
netplan try is the answer to that danger, and on a remote server it is the command to reach for by default. It applies the configuration, then starts a countdown; if you do not confirm within the timeout, it rolls the change back to what was there before. The default timeout is 120 seconds, adjustable with --timeout:
netplan try --timeout 90
Press Enter to keep the change, or wait and let it revert. If your new config breaks connectivity, you lose the session, never confirm, and two minutes later the server has restored the working configuration and your SSH is reachable again. That auto-revert is the whole point of the command. It is not flawless: some changes involving interface renames or certain backend transitions do not roll back cleanly, which is where a dedicated server with an IPMI or iKVM console earns its keep. Out-of-band console access is the backstop for the day try cannot save you, and it is worth confirming you have it before you need it.
Once the network is up, netplan status shows you what the backend did with your config:
Online state: online
DNS Addresses: 192.0.2.53
DNS Search: example.com
● 2: enp1s0 ethernet UP (networkd: enp1s0)
MAC Address: 52:54:00:11:22:33
Addresses: 192.0.2.10/24
2001:db8::10/64
Routes: default via 192.0.2.1 (static)
default via 2001:db8::1 (static)
(Output is illustrative.) netplan status arrived in version 0.106. On 1.0 and later, netplan status --diff also shows where the running state differs from your YAML, which is the fastest way to catch a config that generated but did not fully apply.
Two more commands read and write the merged model without opening an editor. netplan get prints the current configuration as YAML, and netplan set changes a single value by dotted path:
netplan get ethernets.enp1s0.addresses
netplan set ethernets.enp1s0.dhcp4=false
netplan set writes to /etc/netplan/90-netplan-set.yaml by default, which is handy for scripts and confusing if you forget it did so; check that file if a value appears from nowhere.
Cheat sheet
The tasks you repeat, and the YAML fragment or command for each:
| Task | YAML fragment or command |
|---|---|
| Static IPv4 address | addresses: [192.0.2.10/24] |
| Default gateway (current syntax) | routes: [{to: default, via: 192.0.2.1}] |
| DNS servers | nameservers: {addresses: [192.0.2.53]} |
| Enable DHCP on an interface | dhcp4: true |
| Choose a backend | renderer: networkd |
| Validate config without applying | netplan generate |
| Apply with auto-revert safety | netplan try |
| Apply immediately | netplan apply |
| Inspect live state | netplan status --diff |
| Read one value | netplan get <path> |
VLANs, bonds and bridges
Three constructs come up often enough on servers to keep in reach. Each builds on the same file; the interface types move into their own top-level block.
A tagged VLAN on one uplink
When your provider hands you a tagged VLAN for a private network, define it as a vlans entry that references the physical interface through its link:
network:
version: 2
ethernets:
enp1s0: {}
vlans:
enp1s0.100:
id: 100
link: enp1s0
addresses:
- 198.51.100.10/24
The id is the 802.1Q tag, and link names the parent interface, which stays defined (here with an empty {}) so Netplan brings it up. Untagged traffic still uses the parent; tagged frames for VLAN 100 ride the new logical interface.
Two NICs bonded with LACP
A bond aggregates several links into one logical interface for bandwidth or redundancy. Mode 802.3ad is LACP, and it needs the switch side configured to match:
network:
version: 2
ethernets:
eno1: {}
eno2: {}
bonds:
bond0:
interfaces: [eno1, eno2]
addresses:
- 192.0.2.20/24
routes:
- to: default
via: 192.0.2.1
parameters:
mode: 802.3ad
lacp-rate: fast
mii-monitor-interval: 100
LACP is a two-ended agreement. If the upstream switch ports are not in a matching LACP group, the bond will not come up, so coordinate with whoever runs the switch before you apply this.
A bridge for virtual machines
If the server is a KVM or LXD host, a bridge lets guests share the physical NIC as though plugged into a switch. The host address moves onto the bridge, and the physical interface becomes a bridge port with no address of its own:
network:
version: 2
ethernets:
enp1s0:
dhcp4: false
bridges:
br0:
interfaces: [enp1s0]
addresses:
- 192.0.2.30/24
routes:
- to: default
via: 192.0.2.1
nameservers:
addresses: [192.0.2.53]
parameters:
stp: false
forward-delay: 0
This is the same idea Proxmox implements with its vmbr0, if you have met that. Turning off spanning-tree and the forward delay suits a single-uplink host bridge; leave them on only when the bridge has multiple physical ports that could form a loop.
When configuration does not take: troubleshooting
Most Netplan problems fall into a few buckets.
YAML indentation. Netplan's YAML uses spaces for indentation and rejects tabs outright. A stray tab, or an inconsistent indent, produces a parse error. Run netplan generate after every edit: it is the cheapest validator you have, and it refuses to write broken config rather than applying it.
The change generated but the interface looks wrong. Ask the backend what it did rather than trusting the YAML. ip addr and ip route show the addresses and routes installed on the link; networkctl status enp1s0 shows systemd-networkd's view; resolvectl status shows the DNS servers in effect. If netplan status --diff reports a gap between your file and the running state, the apply did not fully take, and one of those commands will show where.
Your edits vanish after a reboot. This is the cloud-init overwrite, the most common surprise on a dedicated or cloud server. cloud-init regenerates /etc/netplan/50-cloud-init.yaml on boot from its own data source, overwriting whatever it thinks it owns. The supported fix is to tell cloud-init to stop managing the network, once:
echo 'network: {config: disabled}' | \
tee /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
After that, cloud-init leaves /etc/netplan/ alone and your own file becomes the single source of truth. You can then delete or rewrite 50-cloud-init.yaml and it will stay that way. Do this before you invest time in a hand-written config, not after you have fought the same reboot twice.
When you don't have Netplan
Netplan is an Ubuntu default, not a Linux universal. On the RHEL family (RHEL, AlmaLinux, Rocky) and on most desktop distributions, there is no Netplan layer: NetworkManager manages interfaces directly, and you configure it with nmcli, nmtui, or connection files. If your fleet is mixed, the companion NetworkManager crash course covers that side with the same server focus.
Debian is the near-miss. The netplan.io package sits in Debian's repositories, so you can install and use it, but Debian does not use it by default; a stock Debian server still configures networking through ifupdown and /etc/network/interfaces, or NetworkManager on the desktop. If you are weighing the two, the Ubuntu versus Debian comparison goes through where each fits, and the broader distro-choice guide sets Netplan-by-default against the alternatives.
Deploying on Serverside
Netplan is only as useful as the network under it. A Serverside dedicated server runs on our own network (AS55285), so the static IPv4 and IPv6 addresses you write into that YAML sit behind routing we control, with always-on DDoS mitigation in front of the public interface. Provisioning is sub-minute: a fresh Ubuntu server is deployed in under a minute, an LTS image with Netplan already in place, ready before you have finished drafting the config it will run.
FAQ
What is the difference between netplan apply and netplan try?
netplan apply regenerates the backend config and applies it to the live system straight away, with no way back if it breaks your connection. netplan try applies the same config but starts a 120-second countdown and rolls the change back automatically unless you press Enter to confirm. On a remote server, reach for try when you change anything that could affect the link you are connected over, and keep apply for changes you are certain about or are making from the console.
Why do my network changes disappear after a reboot?
Almost always cloud-init. On cloud and dedicated-server images, cloud-init regenerates /etc/netplan/50-cloud-init.yaml on every boot from its own data source and overwrites edits it thinks it owns. Stop it by creating /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg containing network: {config: disabled}, then manage /etc/netplan/ yourself. After that your files persist across reboots.
How do I set DNS servers in Netplan?
Add a nameservers block to the interface, with an addresses list and an optional search domain list. The resolvers apply once you run netplan apply or netplan try, and you can confirm them with resolvectl status. On systemd-networkd systems DNS is handled by systemd-resolved, so the servers you set show up there rather than in /etc/resolv.conf directly.
Is Netplan the same as NetworkManager?
No. Netplan is a configuration layer that generates config for a backend; NetworkManager is one of the two backends it can target, the other being systemd-networkd. Ubuntu Server uses the networkd backend by default, Ubuntu Desktop uses NetworkManager, and both are driven through the same Netplan YAML. On systems without Netplan, such as RHEL-family servers, you configure NetworkManager directly instead.
I am migrating from /etc/network/interfaces. Does Netplan replace it?
Yes, on Ubuntu. The old ifupdown stack and its /etc/network/interfaces file are what Netplan replaced when Ubuntu adopted it in 17.10. You do not edit both: translate each iface stanza into the equivalent Netplan ethernets entry with its address, routes and nameservers, and let Netplan render the backend config. Debian still uses /etc/network/interfaces by default, which is one difference to expect when moving between the two.

Written by
Co-founder & CTO, Serverside.com
Jesse is the co-founder and CTO of Serverside.com, where he leads the engineering behind the company's bare-metal cloud: from the ASN 55285 backbone to the core automation that drives day-to-day operation. He writes about dedicated servers, operating systems, and running production workloads on bare metal.
Keep reading
View all articlesEnjoyed this article?
Get new guides and engineering write-ups in your inbox. No spam, unsubscribe anytime.




Canadian and
Dutch