Eric Radman : a Journal

Automated Ubuntu Installation

Ubuntu supports major version upgrades using the do-release-upgrade utility, but in my experience this is not a reliable means of updating the operating system. Automating installation facilitates testing and iterative design of systems configuration.

Although Ubuntu is a derivative of Debian, it doesn't follow the same convetions for an automated install.

x86_64 hardware and hypervisors usually don't provide a means of picking a one-time boot device. To force a reinstall wipe the first part of the disk.

# reinstall
# use with caution!
dd if=/dev/zero of=/dev/sda bs=1M count=100
reboot

BIOS PXE Boot

The initial ramdisk and kernel can be taken from the install ISO

mkdir -p /tftpboot/ubuntu22
cp casper/{vmlinuz,initrd} /tftpboot/ubuntu22/
# /tftpboot/pxelinux.cfg/01-00-0c-29-f9-6d-4e
DEFAULT menu.c32
PROMPT 0
TIMEOUT 10
MENU TITLE PXE Menu
LABEL Install Ubuntu 22 Server
  KERNEL ubuntu22/vmlinuz
  APPEND initrd=ubuntu22/initrd ip=dhcp cloud-config-url=/dev/null url=http://192.168.2.20/ubuntu-22.04.1-live-server-amd64.iso autoinstall ds=nocloud-net;s=http://192.168.2.20/ubuntu-srv/

Setting cloud-config-url is not required, but avoids a redundant fetch of the install image.

iPXE Loader

If the machine is booting with UEFI and iPXE the configuration is similar

#!ipxe
set base-url http://192.168.2.20/pub
kernel /ubuntu20/vmlinuz initrd=initrd ip=dhcp cloud-config-url=/dev/null url=http://192.168.2.20/ubuntu-20.04.5-live-server-amd64.iso autoinstall ds=nocloud-net;s=http://192.168.2.20/ubuntu-srv/
initrd /ubuntu20/initrd
boot

Server Config

Create two empty files

touch /var/www/htdocs/meta-data
touch /var/www/htdocs/vendor-data

As well as a file called user-data

#cloud-config
autoinstall:
  version: 1
  package_update: false
  package_upgrade: false
  identity:
    hostname: ubuntu-srv
    password: $crypted_pass
    username: ubuntu
  locale: en_US.UTF-8
  ssh:
    allow-pw: false
    authorized-keys: ['ssh-ed25519 ...']
    install-server: true
  packages:
    - net-tools
    - tmux
  user-data:
    timezone: America/New_York
  late-commands:
    - |
      awk -F= '/HOSTNAME/ { print $2 }' /var/run/systemd/netif/leases/? > /target/etc/hostname
    - |
      echo 'eradman ALL=(ALL) NOPASSWD:ALL' > /target/etc/sudoers.d/ubuntu-nopw
      chmod 440 /target/etc/sudoers.d/ubuntu-nopw

Notes

Minimal Workstation

#cloud-config
autoinstall:
  version: 1
  package_update: false
  package_upgrade: false
  identity:
    hostname: ubuntu-wks
    password: $crypted_pass
    username: ubuntu
  locale: en_US.UTF-8
  ssh:
    allow-pw: false
    authorized-keys: ['ssh-ed25519 ...']
    install-server: true
  packages:
    - adwaita-icon-theme-full
    - efibootmgr
    - gnome-control-center
    - gnome-session
    - gnome-shell-extension-ubuntu-dock
    - gnome-shell-extensions
    - gnome-terminal
    - nautilus
    - net-tools
  late-commands:
    - |
      awk -F= '/HOSTNAME/ { print $2 }' /var/run/systemd/netif/leases/? > /target/etc/hostname
    - |
      echo 'eradman ALL=(ALL) NOPASSWD:ALL' > /target/etc/sudoers.d/ubuntu-nopw
      chmod 440 /target/etc/sudoers.d/ubuntu-nopw
    - |
      /target/bin/efibootmgr | awk '/Boot.+ ubuntu/ { print substr($1, 5, 4) }' | xargs -n1 /target/bin/efibootmgr -o
      true

Notes

Final Steps

After install, change critical: true to optional: true in /etc/netplan/50-cloud-init.yaml if the wired interface is no longer connected.

Ubuntu 20 may not use all available space. Expand the root volume using

lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv
resize2fs /dev/mapper/ubuntu--vg-ubuntu--lv

References