Skip to content

Instantly share code, notes, and snippets.

@snehring
Last active July 2, 2020 01:40
Show Gist options
  • Save snehring/789948765cabb885b09eb61780ba7d3b to your computer and use it in GitHub Desktop.
Save snehring/789948765cabb885b09eb61780ba7d3b to your computer and use it in GitHub Desktop.
ZFS root on Fedora 32 with UEFI

Fedora 32 with ZFS root and UEFI boot

Why

I like ZFS and its features, and I don't want to mess with GRUB.

Requirements

  • Fedora 32+ (Probably) bootable image
  • Dracut 050+ (for improved uefi support and features)
  • ZFS 0.8.4

Process

Boot into your fedora image through whatever means you prefer. You'll want to make sure that the live image has the running kernel verions of kernel-devel and kernel-headers which can be done with: dnf install -y kernel-headers-$(uname -r) kernel-devel-$(uname -r) this will allow dkms to build the zfs modules when you install it.

Install ZFS

Next, install the current zfs release rpm for the version of Fedora that you're installing. At the time of writing that's 32 so: dnf install -y http://download.zfsonlinux.org/fedora/zfs-release.fc32.noarch.rpm and then install zfs dnf install -y zfs and then modprobe zfs afterward.

This should complete succesfully in most cases, but since the live image doesn't have a large amount of storage it may fail.

If it does fail you can usually free up some working space from the earlier dnf steps by doing dnf clean all to clean up all the dnf cache files, and then manually trigger the dkms install process for the running kernel dkms install zfs/0.8.4 -k $(uname -r) and load the zfs module modprobe zfs.

Create zpool and ESP

I'm assuming that the disks you're using here will be used entirely for your root pool (and esp). If this isn't the case you'll want to modify to fit your needs. Additionally I'm assuming you've got two disks and you're going to make a mirror pool. Again if not, just ignore the bits regarding multiple disks.

You need to partition your disks to allow for the creation of a uefi system partition. You can do this with the gdisk utility which should be included with the live image, if it's not just install it with dnf. You'll want to create a new gpt partition table on the disks. Next create two new partitions on each disk, one 500M for the esp with type ef00 (important), and the second the size of the remaining disk space.

Now we'll make a raid1 with mdadm to mirror the esp. This works with a surprising number of uefi motherboards, but it might not for yours. If it doesn't you can always just use one of the esps as /boot/efi instead, it's not perfect, but eh. mdadm --create /dev/md0 --level 1 --raid-disks 2 --metadata 1.0 /dev/nvme0n1p1 /dev/nvme1n1p1 the metadata argument is important here. If you don't include that it puts the metadata at the front of the partition instead of the end which messes up most firmware's detection of the esp. Now you should create the fat32 filesystem on the md device mkfs.vfat -F32 /dev/md0.

Now let's make the zpool. We don't need anything special here other than what you would probably want for a zpool on a linux host zpool create -o ashift=12 -O xattr=sa -O acltype=posixacl -O compression=on -O atime=off -O relatime=off -O mountpoint=none -R /mnt/sysroot rpool mirror /dev/nvme0n1p2 /dev/nvme1n1p2 This will create a pool rpool that is a mirror of the two nvme devices at an altroot located at /mnt/sysroot. Obviously feel free to change your ashfit value as necessary, or enable other features like encryption. Since we're not messing around with grub we can turn on whatever we please. Finally, let's set the fedora dataset as the bootfs zpool set bootfs=rpool/fedora rpool

Install Fedora

At this point you'll have the pool created.

Let's create a dataset for the fedora root to go in. You can do whatever hierachy you like here, I'm lazy so I'm just going to make a rpool/fedora dataset. zfs create -o mountpoint=/ rpool/fedora I'm also going to create a dedicated var and /var/log. You don't have to do this if you don't want to. zfs create -o mountpoint=legacy rpool/fedora/var zfs create -o mountpoint=legacy rpool/fedora/var/log

Let's also create the mountpoint for the esp. mkdir -p /mnt/sysroot/boot/efi and mount it there mount /dev/md0 /mnt/sysroot/boot/efi .

Now we'll actually install fedora dnf --installroot=/mnt/sysroot --releasever=32 group install "Xfce Desktop" this will pull in all the packages needed for the Xfce spin of fedora (more or less). You can choose from any of the other groups available, or even just "Minimal Install" or "Fedora Custom Operating System" whatever you want.

Chroot into the install

At this point we've got a fedora install on our rpool, but we still need to do some stuff to finish up, so let's get things set up to chroot into it.

mount -t proc proc /mnt/sysroot/proc
mount -t sysfs sys /mnt/sysroot/sys
mount -o rbind /dev /mnt/sysroot/dev
cp /etc/resolv.conf /mnt/sysroot/etc
chroot /mnt/sysroot /usr/bin/bash 

now we're in our freshly installed fedora root. At this point let's make sure we've got the same kernel and junk as the live environemnt, for no real reason than consistency. dnf install -y kernel-$(uname -r) kernel-headers-$(uname -r) kernel-devel-$(uname -r) http://download.zfsonlinux.org/fedora/zfs-release.fc32.noarch.rpm busybox efivar efivar-libs mokutil efibootmgr and we can install zfs in the root dnf install -y zfs This should go a head and install and build the modules for the kernel versions that are installed, but just in case it doesn't we can manually run the dkms install for all the installed kernels dkms install zfs/0.8.4 -k <kernel version here> Now it's time for the actual special sauce that makes this possible, dracut. Recent versions of dracut have added the ability to create efi images with the stub kernel, kernel, and initramfs all packaged up nicely. This eliminates the need for a dedicated bootloader, like grub, or even refind. But we need to create some configuration to tell dracut to do this.

uefi=yes
hostonly=yes
hostonly_cmdline=no
use_fstab=yes
early_microcode=yes
add_drivers+=" zfs "
add+=" busybox "
kernel_cmdline="rw rd.shell root=zfs:AUTO" 

You can add any other kernel command line arguments that you want to kernel_cmdline. Now put that in /etc/dracut.conf.d/zfs-uefi.conf and then run dracut --force --regenerate-all which should then recreate initrds for all installed kernels, pull in zfs support and then pull in the kernel, stub, and resulting initrd into an efi executable located (by default) at /boot/efi/EFI/Linux. Assuming everything works out your firmware should be able to see these automagically, but you may have to add them using efibootmgr. efibootmgr --create --disk /dev/nvme0n1p1 --label "Fedora 32 <kernel version>" /EFI/Linux/<Image name here>

Last steps

You'll probably want to set a root password, you'll probably want to also run a restorecon -Rv / to make sure everything is labeled correctly for selinux. You may even need to boot with enforcing=0 on the kernel command line the first time and make a custom policy to cover things that aren't assumed by the default policy. If things take forever to start at boot, or never start, or logins fail, it's probably selinux. Also make sure to add mounts for /boot/efi and any other legacy mounts (like the var/log dataset) to /etc/fstab.

Then you'll want to exit the chroot, unmount all the directories you mounted into the chroot umount /mnt/sysroot/{dev,sys,proc,boot/efi} and then export the pool. Exporting the pool is important here, otherwise the pool will fail to import at boot and drop you to a recovery shell. If that still happens you can always force import the pool in the recovery shell and then continue booting.

At this point we should be done. Ideally we just reboot, the firmware sees the esp and efi image, and boots.

Troubleshooting

raid1 might not work for your mobo, if nothing shows up that's probably what's happened. If this happens, boot in the live environment, copy the esp contents somewhere, stop the md device, wipefs the partitions that made up the md device, mkfs.vfat the individual partitions, and copy the esp contents back. It should show up now.

Panic at boot, not able to find root. If you have rd.shell on the command line it should drop you to a shell and hopefully give you an idea of why it failed. Usually it's the pool has failed to import. You can force it and then exit the shell and the boot process should continue.

Shortcomings

You can't easily modify your kernel command line arguments. You could add rd.cmdline=ask to your kernel_cmdline in the dracut config, which can prompt you for additional arguments. Though I'm not sure if that times out without interaction, I've not played with it yet, so that may not be a good solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment