No network access after Ubuntu 14.04->16.04->18.04 upgrade

Recently I was tending to my fleet of personal servers and I ran into a problem which essentially took my system offline and with it a number of websites I host.

After the upgrade my system did not automatically start eth0 with its assigned static IP address, only the loopback device was configured. Thankfully DigitalOcean provides easy console access. So i started some debugging:

m@box:~$ ifconfig
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

m@box:~$ cat ifconfig-a.log 
eth0: flags=4098<BROADCAST,MULTICAST>  mtu 1500
        ether 04:01:01:8a:1b:01  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Turns out the root cause was no ifup or ifdown, which were both scripts required by /etc/init.d/networking to bring up networking

m@box:~$ ifup eth0
-bash: ifup: command not found
m@box:~$ ifdown eth0
-bash: ifdown: command not found

Excerpt from /etc/init.d/networking

m@box:~$ grep -in "ifup\|ifdown" /etc/init.d/networking 
3:# Provides:          networking ifupdown
17:[ -x /sbin/ifup ] || exit 0
18:[ -x /sbin/ifdown ] || exit 0
101:ifup_hotplug () {
115:		ifup $ifaces "$@" || true
141:	if ifup -a $exclusions $verbose && ifup_hotplug $exclusions $verbose
157:	if ifdown -a --exclude=lo $verbose; then
172:	ifdown -a --exclude=lo $verbose || true
173:	if ifup --exclude=lo $state $verbose ; then
188:	ifdown -a --exclude=lo $verbose || true
191:	if ifup -a --exclude=lo $exclusions $verbose && ifup_hotplug $exclusions $verbose

Temp fix to get back onto the internet to actually install networkd/netplan:

m@box:~$ sudo ifconfig eth0 up
# xx.xx.xx.xx/24 is your static IP
m@box:~$ sudo ip addr add xx.xx.xx.xx/24 dev eth0
# xx.xx.xx.1 is your gateway
m@box:~$ sudo ip route add default via xx.xx.xx.1 dev eth0

long-term fix, install netplan, configure it to manage networkd:

m@box:~$ sudo apt-get install netplan.io

We configure netplan(/etc/netplan/01-netcfg.yaml):

network:
  version: 2
  renderer: networkd
  ethernets:
   eth0:
    dhcp4: no
    dhcp6: no
    addresses: [xx.xx.xx.xx/24]
    gateway4: xx.xx.xx.1
    nameservers:
     addresses: [8.8.8.8, 8.8.4.4]

next we apply the new changes and make sure it gives no errors:

m@box:~$ sudo netplan --debug apply
** (generate:1883): DEBUG: 19:19:36.239: Processing input file //etc/netplan/01-netcfg.yaml..
** (generate:1883): DEBUG: 19:19:36.240: starting new processing pass
** (generate:1883): DEBUG: 19:19:36.240: eth0: setting default backend to 1
** (generate:1883): DEBUG: 19:19:36.244: Generating output files..
** (generate:1883): DEBUG: 19:19:36.244: NetworkManager: definition eth0 is not for us (backend 1)
DEBUG:netplan generated networkd configuration exists, restarting networkd
DEBUG:no netplan generated NM configuration exists
DEBUG:device eth0 operstate is up, not replugging
DEBUG:netplan triggering .link rules for eth0
DEBUG:device lo operstate is unknown, not replugging
DEBUG:netplan triggering .link rules for lo

now the last step is to reboot and make sure the new configuration persists.

And viola, after a reboot everything stayed in place:

m@box:~$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet xx.xx.xx.xx  netmask 255.255.255.0  broadcast xx.xx.xx.255
        inet6 xx::xx:xx:xx:xx  prefixlen 64  scopeid 0x20<link>
        ether 04:01:xx:xx:xx:xx  txqueuelen 1000  (Ethernet)
        RX packets 654  bytes 69420 (69.4 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 517  bytes 108478 (108.4 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0