Running Alpine Linux on ZFS is nothing new as there are multiple guides describing the same. However, I found official setups are either too complicated when it comes to the dataset setup or they simply don't work without legacy boot. What I needed was a simplest way to bring up ZFS on UEFI systems.
First of all, why ZFS? Well, for me it's mostly the matter of detecting issues. While my main server is reasonably well maintained, rest of my lab consists of retired computers I stopped using a long time ago. As such, it's not rare that I have hardware faults and it happened more than once that disk errors went undetected. Hardware faults will still happen with ZFS but at least I will know about them immediately and without corrupting my backups too.
In this case, I will describe my way of bringing up the unencrypted ZFS setup with a separate ext4
boot partition. It requires EFI enabled BIOS with secure boot disabled as Alpine binaries are not signed.
Also, before we start, you'll need Alpine Linux Extended ISO for ZFS installation to work properly. Don't worry, the resulting installation will still be a minimal set of packages.
Once you boot from disk, you can proceed with the setup as you normally would but continue with [none]
at the question about installation disk.
setup-alpine
Since no answer was given, we can proceed with manual steps next. First, we can set up a few variables. While I usually like to use /dev/disk/by-id
for this purpose, Alpine doesn't install eudev
by default. In order to avoid depending on this, I just use good old /dev/sdX
paths.
DISK=/dev/sda
POOL=Tank
Of course, we need some extra packages too. And while we're at it, we might as well load ZFS drivers.
apk add zfs sgdisk e2fsprogs util-linux grub-efi
modprobe zfs
With this out of way, we can partition the disk out. In this example, I use three separate partitions. One for EFI, one for /boot
, and lastly, one for ZFS.
sgdisk --zap-all $DISK
sgdisk -n1:1M:+127M -t1:EF00 $DISK
sgdisk -n2:0:896M -t2:8300 $DISK
sgdisk -n3:0:0 -t3:BF00 $DISK
sgdisk --print $DISK
mdev -s
While having a separate dataset for different directories sometimes makes sense, I usually have rather small installations. Thus, putting everything into a single dataset actually makes sense. Most of the parameters are the usual suspects but do note I am using ashift
13
instead of the more common 12
. My own testing has shown me that on SSD drives, this brings slightly better performance. If you are using this on spinning rust, you can use 12
, but 13
will not hurt performance in any meaningful way, so might as well leave it as is.
zpool create -f -o ashift=13 -o autotrim=on \
-O compression=lz4 -O normalization=formD \
-O acltype=posixacl -O xattr=sa -O dnodesize=auto -O atime=off \
-O canmount=noauto -O mountpoint=/ -R /mnt ${POOL} ${DISK}3
Next is the boot partition, and this one will be ext4
. Yes, having ZFS here would be "purer," but I will sacrifice that purity for the ease of troubleshooting when something goes wrong.
yes | mkfs.ext4 ${DISK}2
mkdir /mnt/boot
mount -t ext4 ${DISK}2 /mnt/boot/
The last partition to format is EFI, and that has to be FAT32
in order to be bootable.
mkfs.vfat -F 32 -n EFI -i 4d65646f ${DISK}1
mkdir /mnt/boot/efi
mount -t vfat ${DISK}1 /mnt/boot/efi
With all that out of the way, we can finally install Alpine onto our disk using the handy setup-disk
script. You can ignore the failed to get canonical path
error as we're going to manually adjust things later.
BOOTLOADER=grub setup-disk -v /mnt
With the system installed, we can chroot into it and continue the rest of the steps from within.
mount --rbind /dev /mnt/dev
mount --rbind /proc /mnt/proc
mount --rbind /sys /mnt/sys
chroot /mnt /usr/bin/env DISK=$DISK POOL=$POOL ash --login
For grub, we need a small workaround first so it properly detects our pool.
sed -i "s|rpool=.*|rpool=$POOL|" /etc/grub.d/10_linux
And then we can properly install the EFI bootloader.
apk add efibootmgr
mkdir -p /boot/efi/alpine/grub-bootdir/x86_64-efi/
grub-install --target=x86_64-efi \
--boot-directory=/boot/efi/alpine/grub-bootdir/x86_64-efi/ \
--efi-directory=/boot/efi \
--bootloader-id=alpine
grub-mkconfig -o /boot/efi/alpine/grub-bootdir/x86_64-efi/grub/grub.cfg
And that's it. We can now exit the chroot environment.
exit
Let's unmount all our partitions.
umount -Rl /mnt
zpool export -a
And, after reboot, your system should come up with ZFS in place.
reboot
Amazing guide! Brief and working.
Thanks a lot!
Hey, first of all a big thank you for your website and this tutorial.
The system boots successfully.
However, there’s a message above the line of “OpenRC 0.52.1 is starting up Linux 6.6.23-0-lts (x86_64)”.
How do I resolve the following error?
> cannot import ‘13956164514112223345’ : a pool with that name already exists
> use the form ‘zpool import ‘ to give it a new name
> cannot import ‘Tank’: a pool with that name already exists
> use the form ‘zpool import ‘ to give it a new name
Hopefully someone will be able to help me.