DB-유

Configuring a Static IP with Netplan on Ubuntu 18.04

· linux, ubuntu, networking, netplan

The first thing that catches people out on Ubuntu 18.04 Server is that /etc/network/interfaces is no longer where the network lives. It still exists, mostly empty, with a comment pointing you at Netplan. Netplan is a YAML front-end that generates configuration for a backend renderer, systemd-networkd on servers and NetworkManager on desktops. The upside is a much shorter config file; the downside is that YAML indentation errors give unhelpful messages and that the interface names are now things like ens160 or enp0s3.

I lost a bit of time to that on my first 18.04 box. Below is how I give a fresh server a static address now, using the netplan try safety net so a typo doesn't cut off my SSH session. Have console access (or some other way back in) and the address, prefix, gateway and DNS servers written down before you start.

Find the interface name and current config

ip -br addr
ip route
ls /etc/netplan/

ip -br addr (brief mode) prints one line per interface. On a VMware guest the NIC is usually ens160, on VirtualBox enp0s3, on KVM ens3. The existing Netplan file is typically /etc/netplan/50-cloud-init.yaml on cloud images and /etc/netplan/01-netcfg.yaml on installs from the ISO. Look at it:

cat /etc/netplan/50-cloud-init.yaml

A DHCP configuration looks like:

network:
    version: 2
    ethernets:
        ens160:
            dhcp4: true

Keep cloud-init from overwriting your changes

If the file is 50-cloud-init.yaml, cloud-init regenerates it on boot from the datasource. Disable that first or your static IP disappears on the next reboot:

sudo tee /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg <<'EOF'
network: {config: disabled}
EOF

Then create your own file rather than editing the cloud-init one. Netplan merges every *.yaml in /etc/netplan/ in lexical order, later files overriding earlier ones for the same interface.

The static configuration

sudo nano /etc/netplan/01-static.yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    ens160:
      dhcp4: no
      addresses:
        - 10.0.1.25/24
      gateway4: 10.0.1.1
      nameservers:
        search: [example.internal]
        addresses: [10.0.1.2, 10.0.1.3]

Points to get right:

  • Indentation must be spaces, never tabs. Two or four is fine as long as it is consistent within the file.
  • addresses takes CIDR notation. There is no separate netmask key.
  • gateway4 is a single address, not a list.
  • renderer: networkd is the server default; state it explicitly so it is obvious later.

If the interface was in the cloud-init file, remove or empty that file so the two do not disagree:

sudo mv /etc/netplan/50-cloud-init.yaml /root/50-cloud-init.yaml.bak

Applying it, with a rollback

I never use netplan apply first on a remote box. netplan try applies the configuration and waits 120 seconds for you to press Enter to confirm. If you do not (because the connection dropped), it reverts automatically:

sudo netplan try

If you are on the console and confident, sudo netplan apply applies it immediately. netplan generate alone writes the backend files under /run/systemd/network/ without applying, which is useful for checking syntax.

Optional: a second interface and static routes

A storage or replication NIC on a private network without a gateway, plus a route to another subnet via that NIC:

    ens192:
      dhcp4: no
      addresses: [192.168.50.25/24]
      routes:
        - to: 192.168.60.0/24
          via: 192.168.50.1

Interfaces without gateway4 still get their directly connected route; only the default route comes from the interface that declares one.

Did it work?

ip -br addr show ens160
ip route show default
systemd-resolve --status | grep -A3 'Link 2'
ping -c 3 10.0.1.1
ping -c 3 archive.ubuntu.com

The DNS check matters because on 18.04 /etc/resolv.conf is a symlink to systemd-resolved's stub file and always shows 127.0.0.53. The real upstream servers are only visible through systemd-resolve --status. If names resolve and the default route is right, the configuration is complete. Reboot once to confirm it survives; that is the real test of the cloud-init change.

Errors and oddities

  • Error in network definition /etc/netplan/01-static.yaml line 6 column 8: expected mapping: YAML indentation problem, often a tab character or a list item at the wrong depth. cat -A shows tabs as ^I.
  • Invalid YAML at ... found character that cannot start any token: same cause, almost always a tab.
  • Configuration applies but reverts after reboot: cloud-init rewrote the file. Confirm the 99-disable-network-config.cfg file from earlier exists and that you did not leave a dhcp4: true for the same interface in another file.
  • Two default routes and flaky connectivity: both interfaces have gateway4. Remove it from the private one.
  • Interface name changed after a hardware change: Netplan supports match: by MAC address plus set-name: to pin a predictable name. Use it when the same YAML has to work across cloned VMs.

I still miss ifcfg-* files a little. But one short YAML file in version control is hard to argue with.