ZFS Root on Ubuntu

ZFS, Native Encryption, root's and you

About

Alike my existing article for a ZFS-rootfs on Archlinux I would also like to provide instructions for achieving the same thing on Ubuntu Server 24.04.2

Please see my main article https://blog.jjgaming.net/blog/zfs-root regarding the use of a ZFS rootfs, reasons to use one and more.

This example makes some assumptions:

  1. It will create an EFI partition and a boot partition before the zfs partition on each disk
  2. It also stores the decryption passphrase key file in the initramfs for automatic unlocking of the zpool at boot time. By omitting this step the host will instead prompt for the passphrase.
  3. Only the first disk will be used for its EFI and boot partitions (The others are also provisioned this way for future proofing but are not used in this example)

Instructions

# Set up some variables

export disks=(/dev/disk/by-id/nvme-CT2000P5PSSD8-00000000001 /dev/disk/by-id/nvme-CT2000P5PSSD8-00000000002 /dev/disk/by-id/nvme-CT2000P5PSSD8-00000000003 /dev/disk/by-id/nvme-CT2000P5PSSD8-00000000004)
export zpooltype=raidz2 # raidz3 # mirror #'' #< stripe
export rootfsPassphrase=123123123
export hostName=myserver
export zpoolName=storage

# Partition the disks with an EFI, boot and ZFS partition
for disk in ${disks[*]} ; do sgdisk ${disk} -o \
-n 1:2M:+1G -t 1:EF00 \
-n 2:0:+2G -t 2:8300  \
-n 3 -t 3:BF01 \
; done

# mkfs.vfat on all first partitions
for part in ${disks[*]/%/-part1} ; do mkfs.vfat -F 32 -n EFI ${part} ; done

# Install ZFS in the live environment
apt install -y zfsutils-linux

# Create our passphrase key and protect it
echo "${rootfsPassphrase}" > /etc/zfs/${zpoolName}.key
chmod 000 /etc/zfs/${zpoolName}.key

# Create our zpool with however many disks
zpool create -f -o ashift=12 \
 -O compression=lz4 \
 -O normalization=formD \
 -O acltype=posixacl \
 -O xattr=sa \
 -O relatime=on \
 -O encryption=aes-256-gcm \
 -O keylocation=file:///etc/zfs/${zpoolName}.key \
 -O keyformat=passphrase \
 -o autotrim=on \
 -o compatibility=openzfs-2.1-linux \
 -m none ${zpoolName} raidz2 ${disks[*]/%/3}

# Create some additional mountpoints and mount them (Optional but often preferred)
zfs create -o mountpoint=/ -o canmount=noauto ${zpoolName}/root
zfs create -o mountpoint=/home ${zpoolName}/home
zfs create -o mountpoint=/var ${zpoolName}/var
zfs create -o mountpoint=/var/log ${zpoolName}/var/log

# Set our bootfs as a hint to the startup environment
zpool set bootfs=${zpoolName}/root ${zpoolName}

# mount the above optional mountpoints
mount -t zfs -o zfsutil ${zpoolName}/root /mnt
mkdir /mnt/home /mnt/var /mnt/var/log
mount -t zfs -o zfsutil ${zpoolName}/home /mnt/home
mount -t zfs -o zfsutil ${zpoolName}/var /mnt/var
mount -t zfs -o zfsutil ${zpoolName}/var/log /mnt/var/log

# Disable automatic mounting for the top level dataset
zfs set canmount=noauto $zpoolName

# mkfs.ext4 on the second partition of the first disk and begin mounting
mkfs.ext4 ${disks[0]}-part2
mount ${disks[0]}-part2 /mnt/boot
###mkfs.msdos -F 32 -n EFI ${disks[0]}-part1
mkdir /mnt/boot/efi
mount ${disks[0]}1 /mnt/boot/efi

# Install debootstrap for our installation and begin the installation
debootstrap $(basename `ls -d /cdrom/dists/*/ | grep -v stable | head -1`) /mnt

# Set up the new environmnet
echo ${hostName} > /mnt/etc/hostname
sed "s/ubuntu/${hostName}/g" /etc/hosts > /mnt/etc/hosts
echo 'deb https://ubuntu.mirror.serversaustralia.com.au/ubuntu/ noble main' > /etc/apt/sources.list # Overwrites
sed '/cdrom/d' /etc/apt/sources.list > /mnt/etc/apt/sources.list
cp /etc/netplan/*.yaml /mnt/etc/netplan/
mkdir -p /mnt/install/etc/NetworkManager/system-connections/
cp /etc/NetworkManager/system-connections/* /mnt/install/etc/NetworkManager/system-connections/

# Prepare to and chroot into the new environment
mount --rbind /dev  /mnt/dev
mount --rbind /proc /mnt/proc
mount --rbind /sys  /mnt/sys
chroot /mnt

## Inside chroot commands

locale-gen --purge "en_US.UTF-8"
update-locale LANG=en_US.UTF-8 LANGUAGE=en_US
dpkg-reconfigure --frontend noninteractive locales

dpkg-reconfigure tzdata
apt update
apt install --yes --no-install-recommends linux-image-generic linux-headers-generic
apt install --yes zfs-initramfs grub-efi-amd64-signed shim-signed

echo "PARTUUID=$(blkid -s PARTUUID -o value /dev/sda2) \
    /boot ext4 noatime,nofail,x-systemd.device-timeout=5s 0 1" >> /etc/fstab
echo "PARTUUID=$(blkid -s PARTUUID -o value /dev/sda1) \
    /boot/efi vfat noatime,nofail,x-systemd.device-timeout=5s 0 1" >> /etc/fstab
cat /etc/fstab

KERNEL=`ls /usr/lib/modules/ | cut -d/ -f1 | sed 's/linux-image-//'`
update-initramfs -u -k $KERNEL

update-grub
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=Ubuntu     --recheck --no-floppy
Unique hits for this page: 17