The purpose of this post is to describe different Ubuntu installer tools and to present Ubuntu 22.04 (Jammy) installing methods for desktop and server.
I assume you have functional DHCP, TFTP, HTTP server.
This example shows how to configure ISC DHCP server to boot a machine using a TFTP server.
allow booting; allow bootp; option arch code 93 = unsigned integer 16; host ubuntu { hardware ethernet xx:xx:xx:xx:xx:xx; if option arch = 00:07 { filename "boot/bootx64.efi"; } else { filename "boot/pxelinux.0"; } next-server x.x.x.x; fixed-address x.x.x.x; }
Get installer ISO file and copy the following grub files to the TFTP directory.
Jammy ISO: /boot/fonts/unicode.pf2 /EFI/boot/bootx64.efi /EFI/boot/grubx64.efi
Download pxelinux packages and extract the following files to the TFTP directory.
$ apt download pxelinux syslinux-common pxelinux: /usr/lib/PXELINUX/pxelinux.0 syslinux-common: /usr/lib/syslinux/modules/bios/ldlinux.c32 /usr/lib/syslinux/modules/bios/libutil.c32 /usr/lib/syslinux/modules/bios/menu.c32
The following example file layout can be used on the TFTP server.
. ├── boot │ ├── bootx64.efi │ ├── grubx64.efi │ ├── grub │ │ ├── font.pf2 │ │ ├── grub.cfg │ │ └── x86_64-efi │ │ ├── command.lst │ │ ├── crypto.lst │ │ ├── fs.lst │ │ └── terminal.lst │ ├── jammy │ │ ├── initrd │ │ └── vmlinuz │ ├── ldlinux.c32 -> syslinux/bios/ldlinux.c32 | ├── libutil.c32 -> syslinux/bios/libutil.c32 | ├── menu.c32 -> syslinux/bios/menu.c32 │ ├── pxelinux.cfg │ │ └── default │ ├── pxelinux.0 | └── syslinux | └── bios │ ├── ldlinux.c32 │ ├── libutil.c32 │ └── menu.c32 └── grub -> boot/grub
Sample configuration for grub in grub.cfg.
set timeout=30 loadfont unicode set menu_color_normal=white/black set menu_color_highlight=black/light-gray menuentry "Install Ubuntu Jammy (22.04)" { set gfxpayload=keep linux /boot/jammy/vmlinuz ip=dhcp cloud-config-url=/dev/null url=http://x.x.x.x/jammy-live-server-amd64.iso autoinstall ds="nocloud-net;s=http://x.x.x.x/jammy/" --- # Don't forget the slash at the end. initrd /boot/jammy/initrd }
An example configuration in pxelinux.cfg/default.
default menu.c32 menu title Ubuntu installer label jammy menu label Install Ubuntu J^ammy (22.04) menu default kernel jammy/vmlinuz initrd jammy/initrd append ip=dhcp cloud-config-url=/dev/null url=http://x.x.x.x/jammy-live-server-amd64.iso autoinstall ds=nocloud-net;s=http://x.x.x.x/jammy/ # Don't forget the slash at the end. prompt 0 timeout 300
At least two files needs to be placed on the webserver.
While Canonical announces autoinstall as a server installer, it can be used to install both server and desktop systems.
Two important differencies from Debian Installer, d-i:
An easy way to obtain a working user-data autoinstall configuration file is installing a machine by hand. Then copy the /var/log/installer/autoinstall-user-data file onto the webserver and customize it.
The following configuration examples are made using a QEMU virtual machine:
Kernel parameters
Partitioning examples are made with UEFI, go to BIOS boot disk layout if you have BIOS.
#cloud-config autoinstall: identity: hostname: jammy-minimal password: $6$gnqbMUzHhQzpDEw.$.cCNVVDsDfj5Feebh.5O4VbOmib7tyjmeI2ZsFP7VK2kWwgJFbfjvXo3chpeAqCgXWVIW9oNQ/Ag85PR0IsKD/ username: ubuntu version: 1
The password is ubuntu. And that can be generated using the following command.
mkpasswd --method=sha-512 ubuntu
#cloud-config autoinstall: identity: hostname: jammy-desktop password: $6$5lpwCLsKLEzMkSJc$keOAhA6aO/5RocGThmhVA7LSNuW911Rx5HHXFEa75oGK20cEdAAgn14H5f5nGeq6QgcSyLPrWcg1.JvjXbhrN/ realname: Ubuntu user username: ubuntu keyboard: layout: hu toggle: null variant: '' locale: hu_HU.UTF-8 storage: config: # Partition table - { ptable: gpt, path: /dev/vda, wipe: superblock, preserve: false, name: '', grub_device: false, type: disk, id: disk-vda } # EFI boot partition - { device: disk-vda, size: 536870912, wipe: superblock, flag: boot, number: 1, preserve: false, grub_device: true, type: partition, id: partition-0 } - { fstype: fat32, volume: partition-0, preserve: false, type: format, id: format-0 } # Linux boot partition - { device: disk-vda, size: 1073741824, wipe: superblock, flag: '', number: 2, preserve: false, grub_device: false, type: partition, id: partition-1 } - { fstype: ext4, volume: partition-1, preserve: false, type: format, id: format-1 } # Partition for LVM, VG - { device: disk-vda, size: -1, wipe: superblock, flag: '', number: 3, preserve: false, grub_device: false, type: partition, id: partition-2 } - { name: ubuntu-vg, devices: [ partition-2 ], preserve: false, type: lvm_volgroup, id: lvm_volgroup-0 } # LV for root - { name: ubuntu-lv, volgroup: lvm_volgroup-0, size: -1, wipe: superblock, preserve: false, type: lvm_partition, id: lvm_partition-0 } - { fstype: ext4, volume: lvm_partition-0, preserve: false, type: format, id: format-2 } # Mount points - { path: /, device: format-2, type: mount, id: mount-2 } - { path: /boot, device: format-1, type: mount, id: mount-1 } - { path: /boot/efi, device: format-0, type: mount, id: mount-0 } # Swapfile on root volume swap: swap: 1G late-commands: - 'echo "ubuntu ALL=(ALL) NOPASSWD:ALL" > /target/etc/sudoers.d/ubuntu-nopw' - chmod 440 /target/etc/sudoers.d/ubuntu-nopw - curtin in-target --target=/target -- sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT=""/GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"/' /etc/default/grub - curtin in-target --target=/target -- apt-get install -y ubuntu-desktop plymouth-theme-ubuntu-logo grub-gfxpayload-lists version: 1
Size of the LVM partition and size of the root LV is -1 which instruct the installer to use all the remaining space. A partition or volume with size: -1 must be the last in the config. In the above example: installer creates the last partition on all the available space and creates the last defined logical volume on all the available space in the volume group.
#cloud-config autoinstall: identity: hostname: jammy-desktop-crypt password: $6$5lpwCLsKLEzMkSJc$keOAhA6aO/5RocGThmhVA7LSNuW911Rx5HHXFEa75oGK20cEdAAgn14H5f5nGeq6QgcSyLPrWcg1.JvjXbhrN/ realname: Ubuntu user username: ubuntu keyboard: layout: hu toggle: null variant: '' locale: hu_HU.UTF-8 storage: config: - { ptable: gpt, path: /dev/vda, wipe: superblock, preserve: false, name: '', grub_device: false, type: disk, id: disk-vda } - { device: disk-vda, size: 536870912, wipe: superblock, flag: boot, number: 1, preserve: false, grub_device: true, type: partition, id: partition-0 } - { fstype: fat32, volume: partition-0, preserve: false, type: format, id: format-0 } - { device: disk-vda, size: 1073741824, wipe: superblock, flag: '', number: 2, preserve: false, grub_device: false, type: partition, id: partition-1 } - { fstype: ext4, volume: partition-1, preserve: false, type: format, id: format-1 } - { device: disk-vda, size: -1, wipe: superblock, flag: '', number: 3, preserve: false, grub_device: false, type: partition, id: partition-2 } # DM crypt - { volume: partition-2, key: ubuntu, preserve: false, type: dm_crypt, id: dm_crypt-0 } - { name: ubuntu-vg, devices: [ dm_crypt-0 ], preserve: false, type: lvm_volgroup, id: lvm_volgroup-0 } - { name: ubuntu-lv, volgroup: lvm_volgroup-0, size: -1, wipe: superblock, preserve: false, type: lvm_partition, id: lvm_partition-0 } - { fstype: ext4, volume: lvm_partition-0, preserve: false, type: format, id: format-2 } - { path: /, device: format-2, type: mount, id: mount-2 } - { path: /boot, device: format-1, type: mount, id: mount-1 } - { path: /boot/efi, device: format-0, type: mount, id: mount-0 } swap: swap: 1G late-commands: - 'echo "ubuntu ALL=(ALL) NOPASSWD:ALL" > /target/etc/sudoers.d/ubuntu-nopw' - chmod 440 /target/etc/sudoers.d/ubuntu-nopw - curtin in-target --target=/target -- sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT=""/GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"/' /etc/default/grub - curtin in-target --target=/target -- apt-get install -y ubuntu-desktop plymouth-theme-ubuntu-logo grub-gfxpayload-lists version: 1
The DM crypt key is ubuntu in cleartext.
#cloud-config autoinstall: identity: hostname: jammy-server password: $6$5lpwCLsKLEzMkSJc$keOAhA6aO/5RocGThmhVA7LSNuW911Rx5HHXFEa75oGK20cEdAAgn14H5f5nGeq6QgcSyLPrWcg1.JvjXbhrN/ realname: Ubuntu user username: ubuntu keyboard: layout: hu toggle: null variant: '' locale: hu_HU.UTF-8 ssh: allow-pw: false authorized-keys: [ '<SSH KEY>' ] install-server: true storage: config: - { ptable: gpt, path: /dev/vda, wipe: superblock, preserve: false, name: '', grub_device: false, type: disk, id: disk-vda } - { device: disk-vda, size: 536870912, wipe: superblock, flag: boot, number: 1, preserve: false, grub_device: true, type: partition, id: partition-0 } - { fstype: fat32, volume: partition-0, preserve: false, type: format, id: format-0 } - { device: disk-vda, size: 1073741824, wipe: superblock, flag: '', number: 2, preserve: false, grub_device: false, type: partition, id: partition-1 } - { fstype: ext4, volume: partition-1, preserve: false, type: format, id: format-1 } - { device: disk-vda, size: -1, wipe: superblock, flag: '', number: 3, preserve: false, grub_device: false, type: partition, id: partition-2 } - { name: ubuntu-vg, devices: [ partition-2 ], preserve: false, type: lvm_volgroup, id: lvm_volgroup-0 } - { name: ubuntu-lv, volgroup: lvm_volgroup-0, size: 10G, wipe: superblock, preserve: false, type: lvm_partition, id: lvm_partition-0 } - { fstype: ext4, volume: lvm_partition-0, preserve: false, type: format, id: format-2 } - { path: /, device: format-2, type: mount, id: mount-2 } - { path: /boot, device: format-1, type: mount, id: mount-1 } - { path: /boot/efi, device: format-0, type: mount, id: mount-0 } # Swap LV - { name: swap, volgroup: lvm_volgroup-0, size: 1073741824B, wipe: superblock, preserve: false, type: lvm_partition, id: lvm_partition-1 } - { fstype: swap, volume: lvm_partition-1, preserve: false, type: format, id: format-3 } - { path: '', device: format-3, type: mount, id: mount-3 } late-commands: - 'echo "ubuntu ALL=(ALL) NOPASSWD:ALL" > /target/etc/sudoers.d/ubuntu-nopw' - chmod 440 /target/etc/sudoers.d/ubuntu-nopw version: 1
An ssh public key can be copied to the newly installed machine. Just replace <SSH KEY> placeholder with your ssh public key (for example with the output of cat ~/.ssh/id_rsa.pub). Password SSH authentication is disabled.
This example describes the setup of a swap LV instead of a swapfile on root volume.
#cloud-config autoinstall: identity: hostname: jammy-server-raid password: $6$5lpwCLsKLEzMkSJc$keOAhA6aO/5RocGThmhVA7LSNuW911Rx5HHXFEa75oGK20cEdAAgn14H5f5nGeq6QgcSyLPrWcg1.JvjXbhrN/ realname: Ubuntu user username: ubuntu keyboard: layout: hu toggle: null variant: '' locale: hu_HU.UTF-8 ssh: allow-pw: false authorized-keys: [ '<SSH KEY>' ] install-server: true storage: config: # Partition table of two disks - { ptable: gpt, path: /dev/vda, wipe: superblock-recursive, preserve: false, name: '', grub_device: false, type: disk, id: disk-vda } - { ptable: gpt, path: /dev/vdb, wipe: superblock-recursive, preserve: false, name: '', grub_device: false, type: disk, id: disk-vdb } # Install GRUB on first disk - { device: disk-vda, size: 536870912, wipe: superblock, flag: boot, number: 1, preserve: false, grub_device: true, type: partition, id: partition-3 } - { fstype: fat32, volume: partition-3, preserve: false, type: format, id: format-2 } # Install GRUB on second disk - { device: disk-vdb, size: 536870912, wipe: superblock, flag: boot, number: 1, preserve: false, grub_device: true, type: partition, id: partition-8 } - { fstype: fat32, volume: partition-8, preserve: false, type: format, id: format-5 } - { device: disk-vda, size: 1073741824, wipe: superblock, flag: '', number: 2, preserve: false, grub_device: false, type: partition, id: partition-9 } - { device: disk-vdb, size: 1073741824, wipe: superblock, flag: '', number: 2, preserve: false, grub_device: false, type: partition, id: partition-10 } - { device: disk-vda, size: -1, wipe: superblock, flag: '', number: 3, preserve: false, grub_device: false, type: partition, id: partition-11 } - { device: disk-vdb, size: -1, wipe: superblock, flag: '', number: 3, preserve: false, grub_device: false, type: partition, id: partition-12 } # RAID1 for boot partition and root volume - { name: md0, raidlevel: raid1, devices: [ partition-10, partition-9 ], spare_devices: [], preserve: false, wipe: superblock, type: raid, id: raid-0 } - { name: md1, raidlevel: raid1, devices: [ partition-11, partition-12 ], spare_devices: [], preserve: false, wipe: superblock, type: raid, id: raid-1 } # An ext4 boot filesystem on MD - { fstype: ext4, volume: raid-0, preserve: false, type: format, id: format-3 } # LVM on MD - { name: vg0, devices: [ raid-1 ], preserve: false, type: lvm_volgroup, id: lvm_volgroup-0 } - { name: lv-0, volgroup: lvm_volgroup-0, size: 10737418240B, wipe: superblock, preserve: false, type: lvm_partition, id: lvm_partition-0 } - { fstype: ext4, volume: lvm_partition-0, preserve: false, type: format, id: format-4 } - { path: /, device: format-4, type: mount, id: mount-4 } - { path: /boot, device: format-3, type: mount, id: mount-3 } # Install GRUB on both disks - { path: /boot/efi, device: format-2, type: mount, id: mount-2 } - { path: /boot/efi2, device: format-5, type: mount, id: mount-5 } # Swap - { name: swap, volgroup: lvm_volgroup-0, size: 1073741824B, wipe: superblock, preserve: false, type: lvm_partition, id: lvm_partition-1 } - { fstype: swap, volume: lvm_partition-1, preserve: false, type: format, id: format-6 } - { path: '', device: format-6, type: mount, id: mount-6 } late-commands: - 'echo "ubuntu ALL=(ALL) NOPASSWD:ALL" > /target/etc/sudoers.d/ubuntu-nopw' - chmod 440 /target/etc/sudoers.d/ubuntu-nopw version: 1
GRUB is installed on both disks and then the two EFI partitions are also mounted under /boot.
An MD RAID1 array is configured for boot and for LVM.
#cloud-config autoinstall: identity: hostname: jammy-server-raid-bond password: $6$5lpwCLsKLEzMkSJc$keOAhA6aO/5RocGThmhVA7LSNuW911Rx5HHXFEa75oGK20cEdAAgn14H5f5nGeq6QgcSyLPrWcg1.JvjXbhrN/ realname: Ubuntu user username: ubuntu keyboard: layout: hu toggle: null variant: '' locale: hu_HU.UTF-8 network: bonds: bond0: addresses: - 192.168.1.5/24 gateway4: 192.168.1.1 interfaces: - ens10 - ens3 nameservers: addresses: - 192.168.1.2 search: - example.com parameters: mode: balance-rr ethernets: ens10: {} ens3: {} version: 2 ssh: allow-pw: false authorized-keys: [ '<SSH KEY>' ] install-server: true storage: config: - { ptable: gpt, path: /dev/vda, wipe: superblock-recursive, preserve: false, name: '', grub_device: false, type: disk, id: disk-vda } - { ptable: gpt, path: /dev/vdb, wipe: superblock-recursive, preserve: false, name: '', grub_device: false, type: disk, id: disk-vdb } - { device: disk-vda, size: 536870912, wipe: superblock, flag: boot, number: 1, preserve: false, grub_device: true, type: partition, id: partition-3 } - { fstype: fat32, volume: partition-3, preserve: false, type: format, id: format-2 } - { device: disk-vdb, size: 536870912, wipe: superblock, flag: boot, number: 1, preserve: false, grub_device: true, type: partition, id: partition-8 } - { fstype: fat32, volume: partition-8, preserve: false, type: format, id: format-5 } - { device: disk-vda, size: 1073741824, wipe: superblock, flag: '', number: 2, preserve: false, grub_device: false, type: partition, id: partition-9 } - { device: disk-vdb, size: 1073741824, wipe: superblock, flag: '', number: 2, preserve: false, grub_device: false, type: partition, id: partition-10 } - { device: disk-vda, size: -1, wipe: superblock, flag: '', number: 3, preserve: false, grub_device: false, type: partition, id: partition-11 } - { device: disk-vdb, size: -1, wipe: superblock, flag: '', number: 3, preserve: false, grub_device: false, type: partition, id: partition-12 } - { name: md0, raidlevel: raid1, devices: [ partition-10, partition-9 ], spare_devices: [], preserve: false, wipe: superblock, type: raid, id: raid-0 } - { name: md1, raidlevel: raid1, devices: [ partition-11, partition-12 ], spare_devices: [], preserve: false, wipe: superblock, type: raid, id: raid-1 } - { fstype: ext4, volume: raid-0, preserve: false, type: format, id: format-3 } - { name: vg0, devices: [ raid-1 ], preserve: false, type: lvm_volgroup, id: lvm_volgroup-0 } - { name: lv-0, volgroup: lvm_volgroup-0, size: 10737418240B, wipe: superblock, preserve: false, type: lvm_partition, id: lvm_partition-0 } - { fstype: ext4, volume: lvm_partition-0, preserve: false, type: format, id: format-4 } - { path: /, device: format-4, type: mount, id: mount-4 } - { path: /boot, device: format-3, type: mount, id: mount-3 } - { path: /boot/efi, device: format-2, type: mount, id: mount-2 } - { path: /boot/efi2, device: format-5, type: mount, id: mount-5 } - { name: swap, volgroup: lvm_volgroup-0, size: 1073741824B, wipe: superblock, preserve: false, type: lvm_partition, id: lvm_partition-1 } - { fstype: swap, volume: lvm_partition-1, preserve: false, type: format, id: format-6 } - { path: '', device: format-6, type: mount, id: mount-6 } late-commands: - 'echo "ubuntu ALL=(ALL) NOPASSWD:ALL" > /target/etc/sudoers.d/ubuntu-nopw' - chmod 440 /target/etc/sudoers.d/ubuntu-nopw version: 1
A complete netplan configuration is found in this autoinstall example using two cards in bond.
Partition layout needs to be changed when system firmware is BIOS. Partition table type is still GPT, but first partition is a special BIOS boot partition.
storage: config: # Install GRUB in MBR - { ptable: gpt, path: /dev/vda, wipe: superblock, preserve: false, name: '', grub_device: true, type: disk, id: disk-vda } # BIOS boot partition - { device: disk-vda, size: 1048576, flag: bios_grub, number: 1, preserve: false, grub_device: false, type: partition, id: partition-0 } # boot - { device: disk-vda, size: 1073741824, wipe: superblock, flag: '', number: 2, preserve: false, grub_device: false, type: partition, id: partition-1 } - { fstype: ext4, volume: partition-1, preserve: false, type: format, id: format-1 } - { device: disk-vda, size: -1, wipe: superblock, flag: '', number: 3, preserve: false, grub_device: false, type: partition, id: partition-2 } - { name: ubuntu-vg, devices: [ partition-2 ], preserve: false, type: lvm_volgroup, id: lvm_volgroup-0 } - { name: ubuntu-lv, volgroup: lvm_volgroup-0, size: -1, wipe: superblock, preserve: false, type: lvm_partition, id: lvm_partition-0 } - { fstype: ext4, volume: lvm_partition-0, preserve: false, type: format, id: format-2 } - { path: /, device: format-2, type: mount, id: mount-2 } - { path: /boot, device: format-1, type: mount, id: mount-1 }
#cloud-config chpasswd: list: - installer:$6$gnqbMUzHhQzpDEw.$.cCNVVDsDfj5Feebh.5O4VbOmib7tyjmeI2ZsFP7VK2kWwgJFbfjvXo3chpeAqCgXWVIW9oNQ/Ag85PR0IsKD/ ssh_authorized_keys: - <SSH KEY> autoinstall: identity: hostname: jammy-minimal password: $6$gnqbMUzHhQzpDEw.$.cCNVVDsDfj5Feebh.5O4VbOmib7tyjmeI2ZsFP7VK2kWwgJFbfjvXo3chpeAqCgXWVIW9oNQ/Ag85PR0IsKD/ username: ubuntu version: 1
The chpasswd and ssh_authorized_keys cloud-init modules configure installer credentials. Be aware, that chpasswd, ssh_authorized_keys and autoinstall are on the same level. Password is ubuntu.
Installing Ubuntu on cheap Acer Aspire notebooks can be a bit tricky but not impossible.
Setting up static IP configuration, DNS resolver and Open vSwitch on Ubuntu 18.04: netplan, ifupdown, systemd-networkd.