Migrating to root on ZFS in a nutshell

2022-01-20

There are plenty of guides on how to set up root on ZFS but they are all long-winded and none seem to explain how to migrate an existing system.

When using ZFS as our root filesystem we have to ensure that:

1. Creating the pool

Obviously, we first need to create the pool where we will put our system on:

mkdir /mnt/rpool
zpool create rpool /dev/sdX -O compression=lz4 -R /mnt/rpool

Create any datasets now. I don’t know if this is actually required but I like having a separation between “user” and “system” data:

# mountpoint=/ isn't strictly necessary but is useful
zfs create -o mountpoint=/ -o canmount=noauto rpool/system
zfs create -o mountpoint=/home rpool/user

Set the system dataset as the bootfs so the bootloader knows which set to mount at the root:

zpool set bootfs=rpool/system rpool

Unmount user and then mount system and user so system is mounted first:

zfs unmount rpool/user
zfs mount rpool/system
zfs mount rpool/user

Now is a good time to check if the mountpoints are correct:

$ df -h
...
rpool/system          899G  128K  899G   1% /mnt/rpool
rpool/user            899G  128K  899G   1% /mnt/rpool/home

2. Copy data

Copy all data on your system to the pool. Be careful not to include any special filesystems though!

rsync -rav \
    /bin \
    /boot \
    /etc \
    /home \
    /initrd.img \
    /initrd.img.old \
    /lib \
    /lib32 \
    /lib64 \
    /libx32 \
    /opt \
    /root \
    /sbin \
    /usr \
    /var \
    /vmlinuz \
    /vmlinuz.old \
    /mnt/rpool
(cd /mnt/rpool && mkdir tmp dev proc sys run mnt)

In the meantime, grab a coffee.

3. Configure boot loader

We need to make sure the boot loader can actually read the ZFS partition.

First, chroot to the pool:

mount --bind /dev /mnt/rpool/dev
mount --bind /proc /mnt/rpool/proc
mount --bind /sys /mnt/rpool/sys
chroot /mnt/rpool

If you’re using Debian, install zfs-initramfs first:

apt install zfs-initramfs

Install GRUB (for UEFI systems!):

mkdosfs -F 32 -s 1 -n EFI /dev/sdX9
mkdir /boot/efi
echo UUID=`blkid -s UUID -o value /dev/sdX9` /boot/efi vfat defaults 0 0 >> /etc/fstab
mount /boot/efi
apt reinstall grub-efi-amd64 shim-signed

Check if grub recognizes the boot filesystem:

$ grub-probe /boot
zfs

Edit GRUB configuration (`/etc/default/grub´):

GRUB_CMDLINE="root=ZFS=rpool/system"

Update initrd and GRUB:

update-initramfs -c -k all
update-grub

Install GRUB to ESP:

grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=debian --recheck --no-floppy

4. Reboot

The pool should now be bootable. Reboot and run df -h:

...
rpool/system          788G   13G  775G   2% /
...
/dev/sdX9             7.9M  3.4M  4.5M  43% /boot/efi
rpool/user            887G  112G  775G  13% /home
...

5. Resources + more things to do

Now that ZFS is installed, you can make snapshots & backups of the entire system with Sanoid / Syncoid.