ubuntu-netplan
Netplan is Ubuntu's network-configuration abstraction: declarative YAML under
/etc/netplan/ that netplan renders to a backend — systemd-networkd (servers,
the default) or NetworkManager (desktop) — and uses to bring the network up.
This skill is authoring-led (produce correct YAML from a description) with a strong
validation/debug path, aimed at on-prem / air-gapped Ubuntu Server LTS.
Netplan is also the shared network substrate for the sibling skills: cloud-init's
network-config v2 is netplan format, and an autoinstall network: block is
netplan v2. See Boundaries at the end.
Authoring workflow
- Confirm target & renderer. Server →
renderer: networkd (the default).
Desktop/Wi-Fi → renderer: NetworkManager. Check the running version with
netplan info (24.04 ships ~1.0.x, 26.04 tracks 1.2.x — see version notes).
- Pick the device type(s):
ethernets, bonds, bridges, vlans, vrfs,
tunnels, dummy-devices, virtual-ethernets, wifis, modems. Stack them
bottom-up: ethernets → bond → vlans-on-bond → bridges-on-bond/vlan.
- Write the YAML under
/etc/netplan/, choosing a filename that orders
correctly (see precedence). Use 2-space indentation, never tabs.
- Validate without applying:
netplan generate (fails loudly on errors).
- Apply safely. On a remote/SSH host use
netplan try (auto-rollback), never
a blind netplan apply — see "Apply safely".
- Verify:
netplan status --all, ip addr, ip route, and reachability.
The envelope
Every file is wrapped in:
network:
version: 2 # only 2 is accepted (v1 does not exist for netplan)
renderer: networkd # default if omitted; or NetworkManager
ethernets: { ... }
bonds: { ... }
# ...other device types
File model & precedence (a real source of bugs)
- Files live in
/etc/netplan/*.yaml (also /run/netplan/ > /etc/netplan/ >
/lib/netplan/ by directory priority).
- Same filename in a higher-priority dir fully replaces the lower one.
- Different filenames are merged in lexicographic order of the basename, across
all directories. So
20-foo.yaml overrides 10-bar.yaml even if 10-bar.yaml
sits in a higher-priority directory.
- Per-key merge: scalars → later wins; sequences are concatenated, not
replaced (two files each listing
addresses: will accumulate both — a classic
"why do I have an extra IP?" trap); mappings → merged key by key.
- Conventional files commonly present:
50-cloud-init.yaml (written by cloud-init),
00-installer-config.yaml (Subiquity), 90-NM-<uuid>.yaml (NetworkManager
desktop). Use a high number like 99-*.yaml for hand overrides.
- Permissions: netplan only warns if a file is group/other-readable; it does
not refuse or auto-fix. YAML can hold Wi-Fi/WireGuard/EAP secrets, so
chmod 600 /etc/netplan/*.yaml, root-owned. Files netplan writes itself
(netplan set) are already 0600.
Quick patterns (the on-prem bread-and-butter)
Static IPv4 server (no DHCP — the air-gapped default):
network:
version: 2
renderer: networkd
ethernets:
enp1s0:
dhcp4: false
dhcp6: false
addresses: [10.0.10.20/24]
routes:
- to: default # NOT the deprecated gateway4:
via: 10.0.10.1
nameservers:
addresses: [10.0.10.2, 10.0.10.3]
search: [corp.internal]
Bond (802.3ad / LACP):
network:
version: 2
bonds:
bond0:
interfaces: [enp1s0, enp2s0]
parameters:
mode: 802.3ad
mii-monitor-interval: 1000 # no suffix = milliseconds
lacp-rate: fast
addresses: [10.0.10.20/24]
routes: [{to: default, via: 10.0.10.1}]
Bridge for a KVM/LXD host (put the host IP on the bridge so guests attach):
network:
version: 2
ethernets:
enp1s0: {dhcp4: false}
bridges:
br0:
interfaces: [enp1s0]
parameters: {stp: true, forward-delay: 4}
addresses: [10.0.10.20/24]
routes: [{to: default, via: 10.0.10.1}]
nameservers: {addresses: [10.0.10.2]}
VLAN:
network:
version: 2
ethernets:
enp1s0: {dhcp4: false}
vlans:
vlan40:
id: 40
link: enp1s0
addresses: [10.0.40.20/24]
For the full schema (every device type and property), the bond/bridge parameter
tables, and a complete bonded-VLAN-bridge virtualization-host config, read
references/schema.md and references/examples.md.
CLI essentials
| Command |
Purpose |
netplan generate |
Render YAML → backend files. Doubles as the validator. |
netplan apply |
Generate, then apply to the running system. |
netplan try [--timeout 120] |
Apply with auto-rollback if not confirmed. Use over SSH. |
netplan get [path] |
Show the merged config (e.g. netplan get ethernets.enp1s0.addresses). |
netplan set k=v |
Write a key into /etc/netplan/ (validated; new files 0600). |
netplan status [--all] [--diff] |
Show running state; --diff compares system vs YAML. |
netplan info |
Show version + supported feature flags. |
--debug is a global flag placed before the subcommand: netplan --debug apply.
Apply safely (don't lock yourself out)
netplan apply can drop the SSH session. Prefer:
netplan generate # validate first
netplan try --timeout 120 # applies, then rolls back unless confirmed at the prompt
netplan apply also does not remove stale virtual devices (a bond/bridge removed
from YAML lingers until ip link delete dev <name> or reboot). See
references/cli-and-debugging.md for the --state backup-diff workaround.
Validation & debugging (fast path)
netplan generate — catches YAML/schema errors with a location.
netplan get — confirm the merged result (remember sequences concatenate
across files).
netplan --debug apply — see backend invocation.
- Inspect the actual emitted backend config in
/run/systemd/network/10-netplan-*.
networkctl status <iface> and netplan status --diff — carrier, routes, DNS,
system-vs-YAML divergence.
Common "applied but no connectivity": off-subnet gateway needs on-link: true;
duplicate default-route metrics; a stale virtual device from a prior apply;
renderer mismatch. Full playbook in references/cli-and-debugging.md.
Air-gapped / on-prem notes
- Static everything: set
dhcp4: false, dhcp6: false; for a truly static link
also accept-ra: false and link-local: [] to silence autoconfiguration.
- Point
nameservers.addresses at internal resolvers and set search: to the
local domain. Set search: [] on bridges that should not inherit domains.
- Mark disconnected/optional NICs
optional: true (networkd) so boot doesn't wait
~2 minutes for carrier.
- Pin predictable names with
match: {macaddress: ...} + set-name:. Match by
MAC (not name) whenever setting mtu, wakeonlan, macaddress, or offloads —
name-only matching races udev renaming on networkd.
Boundaries (when to hand off to a sibling skill)
- The network block of an autoinstall config is netplan v2. When working inside
an
autoinstall: document, author the network: block with this skill, but use
the ubuntu-autoinstall skill for the surrounding install schema.
- cloud-init network-config v2 is netplan format; cloud-init writes
/etc/netplan/50-cloud-init.yaml on first boot. To stop cloud-init managing the
network and own /etc/netplan directly, drop
/etc/cloud/cloud.cfg.d/99-disable-network-config.cfg containing
network: {config: disabled}. For first-boot/cloud-config concerns use the
ubuntu-cloud-init skill.
Reference files
references/schema.md — full YAML schema: all device types, every addressing
property, complete bond/bridge/vlan/tunnel/vrf parameter tables. (Has a TOC.)
references/examples.md — copy-paste-ready realistic server configs
(static, multi-NIC multi-gateway, bond+VLAN+bridge VM host, source routing,
MAC-matching/rename).
references/cli-and-debugging.md — full CLI reference, the debugging playbook,
and 24.04-vs-26.04 version notes.
1---2name: ubuntu-netplan3description: Author, validate, and debug netplan network configuration (`/etc/netplan/*.yaml`) for Ubuntu Server LTS 24.04 and 26.04, focused on on-premise and air-gapped hosts — static addressing, bonds, bridges, VLANs, VRFs, routing/policy-routing, DNS, interface matching/renaming, and the systemd-networkd renderer (NetworkManager and desktop covered briefly). Also the shared `network:` substrate for the ubuntu-autoinstall and ubuntu-cloud-init skills, which both use netplan v2.4---56# ubuntu-netplan78Netplan is Ubuntu's network-configuration abstraction: declarative YAML under9`/etc/netplan/` that netplan renders to a backend — **systemd-networkd** (servers,10the default) or **NetworkManager** (desktop) — and uses to bring the network up.11This skill is authoring-led (produce correct YAML from a description) with a strong12validation/debug path, aimed at **on-prem / air-gapped Ubuntu Server LTS**.1314Netplan is also the shared network substrate for the sibling skills: cloud-init's15network-config **v2 is netplan format**, and an autoinstall `network:` block **is**16netplan v2. See **Boundaries** at the end.1718## Authoring workflow19201. **Confirm target & renderer.** Server → `renderer: networkd` (the default).21 Desktop/Wi-Fi → `renderer: NetworkManager`. Check the running version with22 `netplan info` (24.04 ships ~1.0.x, 26.04 tracks 1.2.x — see version notes).232. **Pick the device type(s):** `ethernets`, `bonds`, `bridges`, `vlans`, `vrfs`,24 `tunnels`, `dummy-devices`, `virtual-ethernets`, `wifis`, `modems`. Stack them25 bottom-up: ethernets → bond → vlans-on-bond → bridges-on-bond/vlan.263. **Write the YAML** under `/etc/netplan/`, choosing a filename that orders27 correctly (see precedence). Use 2-space indentation, **never tabs**.284. **Validate** without applying: `netplan generate` (fails loudly on errors).295. **Apply safely.** On a remote/SSH host use `netplan try` (auto-rollback), never30 a blind `netplan apply` — see "Apply safely".316. **Verify:** `netplan status --all`, `ip addr`, `ip route`, and reachability.3233## The envelope3435Every file is wrapped in:3637```yaml38network:39 version: 2 # only 2 is accepted (v1 does not exist for netplan)40 renderer: networkd # default if omitted; or NetworkManager41 ethernets: { ... }42 bonds: { ... }43 # ...other device types44```4546## File model & precedence (a real source of bugs)4748- Files live in `/etc/netplan/*.yaml` (also `/run/netplan/` > `/etc/netplan/` >49 `/lib/netplan/` by directory priority).50- **Same filename in a higher-priority dir fully replaces** the lower one.51- **Different filenames are merged in lexicographic order of the basename**, across52 all directories. So `20-foo.yaml` overrides `10-bar.yaml` even if `10-bar.yaml`53 sits in a higher-priority directory.54- **Per-key merge:** scalars → later wins; **sequences are concatenated, not55 replaced** (two files each listing `addresses:` will *accumulate* both — a classic56 "why do I have an extra IP?" trap); mappings → merged key by key.57- Conventional files commonly present: `50-cloud-init.yaml` (written by cloud-init),58 `00-installer-config.yaml` (Subiquity), `90-NM-<uuid>.yaml` (NetworkManager59 desktop). Use a high number like `99-*.yaml` for hand overrides.60- **Permissions:** netplan only *warns* if a file is group/other-readable; it does61 not refuse or auto-fix. YAML can hold Wi-Fi/WireGuard/EAP secrets, so62 `chmod 600 /etc/netplan/*.yaml`, root-owned. Files netplan writes itself63 (`netplan set`) are already `0600`.6465## Quick patterns (the on-prem bread-and-butter)6667**Static IPv4 server** (no DHCP — the air-gapped default):6869```yaml70network:71 version: 272 renderer: networkd73 ethernets:74 enp1s0:75 dhcp4: false76 dhcp6: false77 addresses: [10.0.10.20/24]78 routes:79 - to: default # NOT the deprecated gateway4:80 via: 10.0.10.181 nameservers:82 addresses: [10.0.10.2, 10.0.10.3]83 search: [corp.internal]84```8586**Bond (802.3ad / LACP):**8788```yaml89network:90 version: 291 bonds:92 bond0:93 interfaces: [enp1s0, enp2s0]94 parameters:95 mode: 802.3ad96 mii-monitor-interval: 1000 # no suffix = milliseconds97 lacp-rate: fast98 addresses: [10.0.10.20/24]99 routes: [{to: default, via: 10.0.10.1}]100```101102**Bridge for a KVM/LXD host** (put the host IP on the bridge so guests attach):103104```yaml105network:106 version: 2107 ethernets:108 enp1s0: {dhcp4: false}109 bridges:110 br0:111 interfaces: [enp1s0]112 parameters: {stp: true, forward-delay: 4}113 addresses: [10.0.10.20/24]114 routes: [{to: default, via: 10.0.10.1}]115 nameservers: {addresses: [10.0.10.2]}116```117118**VLAN:**119120```yaml121network:122 version: 2123 ethernets:124 enp1s0: {dhcp4: false}125 vlans:126 vlan40:127 id: 40128 link: enp1s0129 addresses: [10.0.40.20/24]130```131132For the full schema (every device type and property), the bond/bridge parameter133tables, and a complete bonded-VLAN-bridge virtualization-host config, read134`references/schema.md` and `references/examples.md`.135136## CLI essentials137138| Command | Purpose |139|---|---|140| `netplan generate` | Render YAML → backend files. **Doubles as the validator.** |141| `netplan apply` | Generate, then apply to the running system. |142| `netplan try [--timeout 120]` | Apply with **auto-rollback** if not confirmed. Use over SSH. |143| `netplan get [path]` | Show the merged config (e.g. `netplan get ethernets.enp1s0.addresses`). |144| `netplan set k=v` | Write a key into `/etc/netplan/` (validated; new files `0600`). |145| `netplan status [--all] [--diff]` | Show running state; `--diff` compares system vs YAML. |146| `netplan info` | Show version + supported feature flags. |147148`--debug` is a **global** flag placed *before* the subcommand: `netplan --debug apply`.149150## Apply safely (don't lock yourself out)151152`netplan apply` can drop the SSH session. Prefer:153154```bash155netplan generate # validate first156netplan try --timeout 120 # applies, then rolls back unless confirmed at the prompt157```158159`netplan apply` also **does not remove stale virtual devices** (a bond/bridge removed160from YAML lingers until `ip link delete dev <name>` or reboot). See161`references/cli-and-debugging.md` for the `--state` backup-diff workaround.162163## Validation & debugging (fast path)1641651. `netplan generate` — catches YAML/schema errors with a location.1662. `netplan get` — confirm the *merged* result (remember sequences concatenate167 across files).1683. `netplan --debug apply` — see backend invocation.1694. Inspect the actual emitted backend config in `/run/systemd/network/10-netplan-*`.1705. `networkctl status <iface>` and `netplan status --diff` — carrier, routes, DNS,171 system-vs-YAML divergence.172173Common "applied but no connectivity": off-subnet gateway needs `on-link: true`;174duplicate default-route metrics; a stale virtual device from a prior apply;175renderer mismatch. Full playbook in `references/cli-and-debugging.md`.176177## Air-gapped / on-prem notes178179- Static everything: set `dhcp4: false`, `dhcp6: false`; for a truly static link180 also `accept-ra: false` and `link-local: []` to silence autoconfiguration.181- Point `nameservers.addresses` at internal resolvers and set `search:` to the182 local domain. Set `search: []` on bridges that should not inherit domains.183- Mark disconnected/optional NICs `optional: true` (networkd) so boot doesn't wait184 ~2 minutes for carrier.185- Pin predictable names with `match: {macaddress: ...}` + `set-name:`. **Match by186 MAC** (not name) whenever setting `mtu`, `wakeonlan`, `macaddress`, or offloads —187 name-only matching races udev renaming on networkd.188189## Boundaries (when to hand off to a sibling skill)190191- **The network block of an autoinstall config is netplan v2.** When working inside192 an `autoinstall:` document, author the `network:` block with this skill, but use193 the **ubuntu-autoinstall** skill for the surrounding install schema.194- **cloud-init network-config v2 is netplan format**; cloud-init writes195 `/etc/netplan/50-cloud-init.yaml` on first boot. To stop cloud-init managing the196 network and own `/etc/netplan` directly, drop197 `/etc/cloud/cloud.cfg.d/99-disable-network-config.cfg` containing198 `network: {config: disabled}`. For first-boot/cloud-config concerns use the199 **ubuntu-cloud-init** skill.200201## Reference files202203- `references/schema.md` — full YAML schema: all device types, every addressing204 property, complete bond/bridge/vlan/tunnel/vrf parameter tables. (Has a TOC.)205- `references/examples.md` — copy-paste-ready realistic server configs206 (static, multi-NIC multi-gateway, bond+VLAN+bridge VM host, source routing,207 MAC-matching/rename).208- `references/cli-and-debugging.md` — full CLI reference, the debugging playbook,209 and 24.04-vs-26.04 version notes.