Installing Arch Linux on encrypted ZFS and ZFSBootMenu
This is a straightforward tutorial on how to install Arch Linux on a ZFS Encrypted zpool, for those who are familiar with the Arch Linux installation process. The focus here is to provide knowledge on the ZFS part of the equation and not to replace the procedures that are already extensively documented at the Arch Wiki.
Some installation steps are redundant and there is plenty of material over the internet including this blog and the Arch Wiki.
##
Motivations
I started to consider that I should use a more flexible filesystem in the sense I could easily revert to a previous state after I’ve had some really bad situations with nvidia, which broke my setup last year:
- You may want to avoid NVIDIA driver 550 if you’re on a laptop
- NVIDIA driver with Linux kernel 6.10 causing kernel oops
These incidents that I’ve documented on the Gaming On Linux community plus three times I had to use a live USB to recover my Arch install because of dkms
error when building the nvidia
module made me think I should implement some mechanism where from the bootloader I could revert to a previous working state.
Also, since it is a laptop, data-at-rest encryption is a must.
##
The Contenders
While I had some previous job experiences with zfs managing Solaris and FreeBSD, I wanted to prioritize the use of any in-tree technologies. These were my options and the rationale why I didn’t pick them:
luks
+lvm
+xfs / ext4
- While a really reliable solution which I have deployed countless VMs and let the snapshot part to be managed by some hypervisor,lvm2
snapshots are cluttered and you have to configure sizes for them and they can be easily damaged when they reach max size. The fact that lvm does not behave well on chained snapshots does not help much and, having to rely on another technology for compression and deduplication (vdo
) just make things overly complicated.- Yes, I’m aware that you can configure
snapshot_autoextend_threshold
andsnapshot_autoextend_percent
in/etc/lvm/lvm.conf
, but that resize might not be online and depending on the size of an Arch Linux update, it is prone to break. Sometimes you can have 2GB worth of updates andlvm
will not resize quickly enough
- Yes, I’m aware that you can configure
luks
+btrfs
- I’ve had some good performance and compression withzstd:7
on my secondary nvme mounted at/data
and auto unlocked through a keyfile in my main disk. That directory only had data I was willing to lose if something goes wrong(Games, Music, Videos) and oh boy, one day I’ve reached one of those unrecoverable errors onbtrfs
that not even ascrub
would fix it.luks
+bcachefs
- Too much drama plus the fact that a filesystem that aims for data to be persistent to use the wordcache
in it’s name is just, weird…
Another limiting factor is that grub2 still does not support the majority of of ciphers from luks2
, and grub
is the only bootloader option that is able to index and boot btrfs
snapshots. Downgrading to luks1
is not an option.
How it feels managing lvm
snapshots:
##
ZFS Drawbacks
Most of the disadvantages of using zfs
on Linux are higlighted on this Arch Wiki article, but, let’s reiterate them:
- ZFS does not encrypt metadata, meaning some basic structures of your pool like dataset, snapshot names, snapshot hierarchy, and deduplication tables (although deduplicated data is encrypted) might be visible with the adequate tools.
- Pool creation requires knowledge of disk geometry. With nvmes, some of the pain point for tuning are gone, but not all of them.
- Swap inside a
zvol
is not possible and it is an old and well-known issue and you should avoid using it as well as using swapfiles inside ZFS. - ZFS has some caveats due to it’s implementation of
aes
for cryptography and had some performance issues in the past that are now fixed. They had to implement their own mechanisms since they can’t touch kernel algorithms because the module is not licensed as GPL. - out-of-tree kernel module. Pretty obvious but, if you don’t want any surprises like your module not being added to the initramfs, pay attention during kernel install procedures or just use
linux-lts
for better compatibility. - If you are a laptop user, disable hibernate(suspend to disk) entirely. Waking your laptop, importing your zfs pool and after that bringing data that is hibernate from swap back to your disk will likely break your zfs pool.
Now that you are aware that risks exist, let’s follow up with the setup process :)
##
Requisites
Either you create a custom Archiso with the zfs
modules or you use the pre-built archzfs-iso. This is needed so you have support on zfs
without having to compile stuff in live environment.
You should also know how to connect to the internet, set your keyboard layout and all these tasks to proceed with an Arch Linux install using the Arch Live iso.
##
Setup
Again, this is not a detailed procedure on Arch Linux installation itself and you should also use the Arch Wiki Installation Guide as a suplementary material to this guide. Our focus here is the ZFS deployment.
Create the following three partitions on your disk:
- First: 512MB, type
ef00
(ESP) - Second: 4GB, type
8309
(Linux Luks). This will hold our automatically encrypted swap partition that will re-encrypted automatically each boot. - Third: Rest of the disk size, type
bf00
(Solaris root) that will be used as our device for ourzpool
.
Generate your /etc/hostid
with the following command
zgenhostid
Store your pool passphrase in a file with the following commands:
echo '<passphrase>' > /etc/zfs/zroot.key
chmod 000 /etc/zfs/zroot.key
Now, create your zpool
while also replacing your pool name and device partition:
zpool create -f -o ashift=12 \
-O compression=lz4 \
-O acltype=posixacl \
-O xattr=sa \
-O relatime=on \
-O encryption=aes-256-gcm \
-O keylocation=file:///etc/zfs/zroot.key \
-O keyformat=passphrase \
-o autotrim=on \
-m none zroot /dev/nvme0n1p3
zroot
is an arbitrary name, please use one at your convenience.
You might want to change ashift=
depending on your hard drive specification. While some modern SSDs perform better with ashift=13
because they work with 8KiB block size, most nvmes report 4KiB sector size to the operating system so, ashift=12
is a good value to “play safe”. Encryption algorithm aes-256-gcm
is great security wise and has good performance in some of my testings with kdiskmark
and fio
.
Create the datasets with the mount points you desire. I’ll go simple here with my root filesystem and /home
:
zfs create -o mountpoint=none zroot/ROOT
zfs create -o mountpoint=/ -o canmount=noauto zroot/ROOT/arch
zfs create -o mountpoint=/home zroot/home
We’re isolating our root mountpoints under zroot/ROOT
so in the future if you want to copy your installation or dual boot with other Linux, it’s easier. Some zfs properties will be shared amongst all root datasets so, better put them under the same umbrella. We’re also setting canmount=noauto
on our Arch dataset because root device mounting will be handled by the bootloader to allow us to have multiple root devices while not having all of them mounted at the seme time.
Now, export the pool and reimport it under /mnt
:
zpool export zroot
zpool import -N -R /mnt zroot
zfs load-key -L prompt zroot
zfs mount zroot/ROOT/arch
zfs mount zroot/home
Format your ESP:
mkfs.vfat -F 32 -n EFI /dev/nvme0n1p1
mkdir /mnt/efi
mount /dev/nvme0n1p1 /mnt/efi
Install arch Linux using pacstrap
. Please consult the Arch Wiki Installation Guide for more details
pacstrap /mnt base linux-lts linux-firmware linux-lts-headers curl vim
Please note that during the pacstrap
process you can install any package you want, and even install microcode related packages, GUIs, web browsers but on this tutorial, we’ll focus on the bare minimum for a machine to boot zfs
.
Install the zfs-dkms
package:
pacstrap /mnt zfs-dkms
Copy the relevant files to your chroot
environment:
cp /etc/hostid /mnt/etc
cp /etc/resolv.conf /mnt/etc
mkdir /mnt/etc/zfs # plese ignore if you get prompted it already exists
cp /etc/zfs/zroot.key /mnt/etc/zfs
cp /etc/pacman.conf /mnt/etc/pacman.conf
Generate the /etc/fstab
file:
genfstab /mnt > /mnt/etc/fstab
Edit /mnt/etc/fstab
and remove all lines except the one containing /efi
.
Chroot into arch:
arch-chroot /mnt
Execute any additional tasks like (but not limited to): Set your timezone, locales, language and keyboard, network configuration, hostname, root password, create your user and set a password, etc.
Prepare your initramfs. Edit /etc/mkinitcpio.conf
and add your zroot.key
to the FILES=
section, and add zfs
in your HOOKS
section before the filesystems
hook:
FILES=(/etc/zfs/zroot.key)
and
HOOKS=(base udev autodetect microcode modconf keyboard keymap block zfs filesystems)
Force generate a new initramfs:
mkinitcpio -P
Set a cachefile for your zpool and bootfs
for your zroot/ROOT/arch
dataset
zpool set cachefile=/etc/zfs/zpool.cache zroot
zpool set bootfs=zroot/ROOT/arch zroot
In the future, if you dual boot other Linux distribution (let’s say zroot/ROOT/debian
for example), you will need to set this property to this dataset as well.
Now, set the kernel parameters to zroot/ROOT
, and those will be inherited by any child dataset (like zroot/ROOT/arch
):
zfs set org.zfsbootmenu:commandline="noresume init_on_alloc=0 rw spl.spl_hostid=$(hostid)" zroot/ROOT
Enable all services that are needed by zfs:
systemctl enable zfs-import-cache zfs-import.target zfs-mount zfs-zed zfs.target
##
Bootloader
I’m using a highly specialized EFI Loader called ZFSBootMenu (ZBM), which is a neat piece of software that manages multiple aspects of ZFS boot environments and supports ZFS native encryption. To download it and enable it as a bootable EFI binary:
mkdir -p /efi/EFI/zbm
curl https://get.zfsbootmenu.org/latest.EFI --output /efi/EFI/zbm/zfsbootmenu.EFI
efibootmgr --disk /dev/nvme0n1 --part 1 --create --label "ZFSBootMenu" --loader '\EFI\zbm\zfsbootmenu.EFI' --unicode "spl_hostid=$(hostid) zbm.timeout=3 zbm.prefer=zroot zbm.import_policy=hostid" --verbose
ZBM is also provided by AUR but it is a dead simple EFI executable and I prefer to manage it outside the package manager.
Now, you just have to reboot and enjoy:
exit
umount /mnt/efi
zpool export zroot
reboot
##
Self-encrypted Swap
There is a really neat procedure to auto format and encrypt your swap that will rotate your key every boot and it is described here.
First, we need to find out what is the partuuid
of /dev/nvme0n1p2
:
ls -l /dev/disk/by-partuuid/ | grep /nvme1n1p2
After that, add this line at the end of your /etc/crypttab
and put the value with the PARTUUID
of your disk:
swap PARTUUID=6eac8d50-xxxx-yyyy-zzzz-83a1877f9c95 /dev/urandom swap,cipher=aes-xts-plain64,size=512,sector-size=4096
This will automatically create a swap device at /dev/mapper/swap
that needs to be added to your /etc/fstab
. Add the following line at the end of your fstab:
/dev/mapper/swap none swap discard 0 0
Done. Now you have a secure swap that not even you know the secrets to unlock it, and it will be rotated every boot.
##
Auto Snapshots
After rebooting and login in, you can check all your zfs
datasets with:
zfs list
To automate the snapshot process daily, you’ll have to edit the zfs-auto-snapshot-daily.service
as root:
systemctl edit zfs-auto-snapshot-daily.service
Create an override entry below the warning that states ### Anything between here and the comment below will become the contents of the drop-in file
:
[Service]
ExecStart=
ExecStart=/bin/zfs-auto-snapshot --skip-scrub --prefix=znap --label=daily --keep=7 //
Explanation: The first empty ExecStart=
will clear that line and it is pretty much needed on systemd override files, while the second one is the new line with the amount of days yout want to keep worth of snapshots. Please change --keep-7
to a value that makes sense to you. The default unit provided value is --keep=31
which is huge
After some days, the output of zfs list -t snapshot
will show you something like this:
zroot/ROOT@znap_2025-08-27-2300_daily 0B - 192K -
zroot/ROOT@znap_2025-08-28-2317_daily 0B - 192K -
zroot/ROOT@znap_2025-08-29-2300_daily 0B - 192K -
zroot/ROOT@znap_2025-08-30-2300_daily 0B - 192K -
zroot/ROOT@znap_2025-08-31-2300_daily 0B - 192K -
zroot/ROOT@znap_2025-09-01-2300_daily 0B - 192K -
zroot/ROOT@znap_2025-09-02-2300_daily 0B - 192K -
zroot/ROOT/archlinux@znap_2025-08-27-2300_daily 206M - 27.5G -
zroot/ROOT/archlinux@znap_2025-08-28-2317_daily 50.6M - 27.4G -
zroot/ROOT/archlinux@znap_2025-08-29-2300_daily 92.6M - 27.5G -
zroot/ROOT/archlinux@znap_2025-08-30-2300_daily 168M - 27.8G -
zroot/ROOT/archlinux@znap_2025-08-31-2300_daily 120M - 26.1G -
zroot/ROOT/archlinux@znap_2025-09-01-2300_daily 125M - 26.4G -
zroot/ROOT/archlinux@znap_2025-09-02-2300_daily 15.4M - 26.7G -
zroot/home@znap_2025-08-27-2300_daily 351M - 979G -
zroot/home@znap_2025-08-28-2317_daily 119M - 979G -
zroot/home@znap_2025-08-29-2300_daily 609M - 979G -
zroot/home@znap_2025-08-30-2300_daily 463M - 921G -
zroot/home@znap_2025-08-31-2300_daily 1.02G - 922G -
zroot/home@znap_2025-09-01-2300_daily 3.06G - 951G -
zroot/home@znap_2025-09-02-2300_daily 45.0M - 892G -
All snapshots will have the @znap_DATE_daily
sufix, indicating they were auto generated.
If you have a dataset you don’t want to automatically snapshot, add the com.sun:auto-snapshot=false
property to that dataset.
Example if I wanted to disable snapshots in my /home
mounted dataset:
zfs set com.sun:auto-snapshot=false zroot/home
##
Scrub
A ZFS scrub is a process that verifies the integrity of all data within a ZFS storage pool by re-reading all blocks and comparing them against their stored checksums. Scrubbing regularly your filesystem is a nice way to check data integrity and keep things healthy.
On Arch linux, the package systemd-zpool-scrub can help you with that maintenance task, and you can enable automatic scrub on your zpool bu hist adding an @DATASET
between the timer unit name and the .timer
sufix:
systemctl status zpool-scrub@zpool.timer
This will enable the timer that trigger a weekly scrub on the zpool
pool.
For laptop users, worry not because this procedure will only run if you are plugged to your power cord.
##
Final Considerations
This is a really narrow-down procedure on how to have zfs as your root filesystem on Arch.
Consider also:
- Deploying Secure Boot with
sbctl
and sign/efi/EFI/zbm/zfsbootmenu.EFI
with your own keys - Take regular backups on another machine with zfs with
zfs send
Extra Resources:
- Practical ZFS is a nice forum full of folks that are really knowledgeable on ZFS and willing to help.
- r/zfs subreddit has some interesting discussions regarding unique setups. It is still reddit so, you may find the usual internet behavior there as well.
On my next topic I will likely share some extra configurations that I’ve made to increase performance of my zfs pool on nvmes.
Stay safe!