It is based on a combination of the following two guides:
- https://github.com/zfsonlinux/pkg-zfs/wiki/HOWTO-install-Ubuntu-16.04-to-a-Native-ZFS-Root-Filesystem
- http://dotfiles.tnetconsulting.net/articles/2016/0327/ubuntu-zfs-native-root.html
Booting Into the Install Environment
To start off, we need to install the ZFS tools and debootstrap which we will use for actually installing the operating system:
apt-add-repository universe
apt-get update
apt-get install --yes zfsutils-linux debootstrap
Partitioning the Disks
Next, we partition the disks. For now we're going to assume a two disk system which is just doing striping, but you can change as desired:parted -- /dev/sda mklabel msdos Y mkpart primary zfs 0% 100%
parted -- /dev/sdb mklabel msdos Y mkpart primary zfs 0% 100%
Device naming during bootup on Linux isn't always static, so we want to reference the disks through a static identifier like disk ID (/dev/disk/by-id), unfortunately Grub fails to properly identify the devices from the zfs command output, so we need this hack for now to make sure that update-grub will correctly identify the disks. Specifically, it forces creation of symlinks in /dev which map to the device names in /dev/disk/by-id. Some additional details about the issue can be found in this grub2 launchpad bug.
echo 'KERNEL=="sd*[!0-9]", IMPORT{parent}=="ID_*", SYMLINK+="$env{ID_BUS}-$env{ID_SERIAL}"
> KERNEL=="sd*[0-9]", IMPORT{parent}=="ID_*", SYMLINK+="$env{ID_BUS}-$env{ID_SERIAL}-part%n"' > /etc/udev/rules.d/90-zfs.rules
udevadm trigger
Create Your Zpool
Now we actually create the zpool and import it to /mnt:zpool create -m none -o ashift=12 -O compression=lz4 rpool /dev/sda1 /dev/sdb1
zfs create -o mountpoint=/ rpool/root
zpool export rpool
zpool import -d /dev/disk/by-id -R /mnt rpool
So here we create the zpool by device name, and then re-import it by device ID while mounting at /mnt.
And then create various partitions off of the root filesystem:
zfs create -o setuid=off rpool/root/home
zfs create -o mountpoint=/root rpool/root/home/root
zfs create -o canmount=off -o setuid=off -o exec=off rpool/root/var
zfs create -o com.sun:auto-snapshot=false rpool/root/var/cache
zfs create rpool/root/var/log
zfs create rpool/root/var/spool
zfs create -o com.sun:auto-snapshot=false -o exec=on rpool/root/var/tmp
We break out these various subdirectories so we have the ability to optionally enable/disable compression, support for setuid/exec, and various other options. We can also choose to limit the maximum size of each mount independently.
Install Ubuntu
debootstrap the OS
cp /etc/udev/rules.d/90-zfs.rules /mnt/etc/udev/rules.d/90-zfs.rules
You'll notice at the end here we copy our hacky udev rule to the new filesystem. The "devices=off" option we set here disables the ability to create device nodes in the filesystem. This works since /dev is actually a devtmpfs partition of its own.