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
-
#cloud-config
is a YAML comment, but seems to be required! The installer will not report the cause of the failure if it is omitted. -
hostname
is required, and unfortunately cannot be set using DHCPoption host-name
. To work around this we useawk
to find the hostname in the DHCP leases file at the end of the installation. -
To break out of the installer and debug commands using a shell
add
false
orreturn 1
at the end ofearly-commands:
orlate-commands
. - Disable package updates to reduce install time. This is useful if the install needs to be repeated several times while debug the configuration.
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
-
autoinstall
will not set the boot order on UEFI systems.
A manual workaround is to find the
ubuntu
label and make this item first in the boot list. Each item identifier is four hex digits.
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