Installing Arch Linux on ZFS

A complete guide to install Arch Linux on ZFS, "The last word in filesystems", step by step.

Introduction

In summary, ZFS is a stable, fast and advanced filesystem with many features such as snapshotting, native encryption and caching in memory. In this post we'll go through the complete process of installing Arch Linux on ZFS, starting with creating a bootable USB drive with ZFS support.

Building an Arch ISO with ZFS support

The default ISO image you can download from the Arch Linux website does not support ZFS. That's why we have to create our own using the archiso tool. We will install the tool from the Arch Linux repositories.

If you are not already running Arch Linux, you can also install and run the tools inside a container. (Remember to remove sudo from all commands)
docker run --rm --privileged -it -v "$(pwd)/out":/root/iso/out archlinux
  1. Install the archiso tool, create a work directory and copy the releng profile to it.
sudo pacman -Sy archiso
mkdir ~/iso
cp -r /usr/share/archiso/configs/releng/* ~/iso
  1. Add the ArchZFS repository to the Pacman configuration for our build and tell archiso to install the ZFS DKMS module and ZFS utils to our resulting ISO.
echo -e '
[archzfs]
Server = https://archzfs.com/$repo/$arch
SigLevel = Optional TrustAll' >> ~/iso/pacman.conf

echo -e '
linux-headers
archzfs-dkms
zfs-utils' >> ~/iso/packages.x86_64
  1. Next, build the ISO. This can take some time…
sudo mkarchiso -vo ~/iso/out ~/iso
  1. Finally, write the ISO to a USB drive using your favorite tool, restart your computer and boot it.

Storage setup

Partitioning

Before getting started, I set my keymap and make the console font larger.

loadkeys de-latin1
setfont ter-132n

Once everything is set up, start parted on the drive you want to install Arch Linux to (/dev/nvme0n1 in my case) and create a new GPT partition table and partitions. Though it's not covered in this post, it should be no problem to install Arch Linux on ZFS alongside existing operating systems. If you want to do this, skip creating a new partition table and partition the disk to your liking.

Creating a new partition table will destroy everything saved on the disk! Make sure to backup important files and to select the correct disk. No really, double check that.
parted -a opt /dev/nvme0n1
print # Display current partition table
mklabel gpt # Create new partition table, will destroy data!
mkpart primary 5MB% 512MB # Boot/EFI
mkpart primary 512MB 100% # remaining space
set 1 boot on # Boot flag
set 1 esp on # EFI flag
quit

Creating the ZFS Pool

Let's create a new ZFS pool named "zroot". The options are a solid default for a pool for day to day desktop use. We'll build our final root filesystem in /mnt, so /mnt in the installer OS will be / in the installed system. Make sure to match the capitalization of the o/O letters!

zpool create \
  -o ashift=12 \
  -O acltype=posixacl -O canmount=off \
  -O dnodesize=auto -O normalization=formD \
  -O atime=off -O xattr=sa -O mountpoint=none \
  -R /mnt zroot /dev/nvme0n1p2 # ← Partition 2

Creating datasets

You need at least one dataset for your root filesystem /. I'm creating an additional dataset for my /home directory, so I can take snapshots of my base system and it separately. You can create additional datasets if you want.

# Root dataset
zfs create -o canmount=noauto -o mountpoint=/ zroot/rootfs

# Set the root dataset as bootfs
zpool set bootfs=zroot/rootfs zroot

# Additional datasets…
zfs create zroot/rootfs/home

Next, mount the root dataset. This will also mount any child datasets you created.

zfs mount zroot/rootfs

Finally, we need to tell ZFS to create a zpool.cache file. This file contains information about out ZFS pool and can be loaded at boot time instead of re-importing the pool each time. Because /etc/zfs is part of the installer, we have to copy the cache file to our soon-to-be /etc/zfs in /mnt.

mkdir -p  /mnt/etc/zfs
zpool set cachefile=/etc/zfs/zpool.cache zroot
cp /etc/zfs/zpool.cache /mnt/etc/zfs/zpool.cache

Setting up the boot partition

Format the boot partition with FAT32 and mount it into /mnt/boot.

mkfs.vfat /dev/nvme0n1p1
mkdir /mnt/boot
mount /dev/nvme0n1p1 /mnt/boot

Generate filesystem table

Now that all filesystems for the new Arch installation are mounted in /mnt, we can generate the final fstab using the genfstab tool.

genfstab -U -p /mnt >> /mnt/etc/fstab

System setup

Install base packages

Use pacstrap to install the base packages and a desktop environment. You can pick another desktop environment or shell and leave out SSH or ZSH if you want to.

pacstrap /mnt base base-devel linux linux-headers linux-firmware grub efibootmgr \
  nano zsh gdm gnome openssh

Setup ZFS

After chrooting into your new system, the first thing to do is to add the ArchZFS repository and install the ZFS DKMS module. After that, you can install additional packages you want in your new system.

arch-chroot /mnt

echo -e '
[archzfs]
Server = https://archzfs.com/$repo/x86_64' >> /etc/pacman.conf

# ArchZFS GPG keys (see https://wiki.archlinux.org/index.php/Unofficial_user_repositories#archzfs)
pacman-key -r DDF7DB817396A49B2A2723F7403BD972F75D9D76
pacman-key --lsign-key DDF7DB817396A49B2A2723F7403BD972F75D9D76

pacman -Sy zfs-dkms

# Optional packages
pacman -S nvidia-dkms intel-ucode

Boot setup

Edit /etc/mkinitcpio.conf, find the line that defines build hooks (HOOKS=(...)) and add the ZFS hooks like this:

HOOKS=(base udev autodetect modconf block keyboard keymap zfs filesystems)

After that, generate the image with

mkinitcpio -p linux

Finally, let's set up GRUB. First, make sure to create the /boot/grub directory. Edit the file /etc/default/grub and add zfs=zroot/rootfs to the kernel parameters at GRUB_CMDLINE_LINUX_DEFAULT. Generate the GRUB configuration files and install GRUB with the according tools:

mkdir /boot/grub
nano /etc/default/grub # GRUB_CMDLINE_LINUX_DEFAULT="zfs=zroot/rootfs"
grub-mkconfig -o /boot/grub/grub.cfg
grub-install --target=x86_64-efi --efi-directory=/boot

Finalizing the installation

We need to enable some services for systemd to be able to handle and mount our ZFS datasets. Remember to enable your display manager service, too (gdm in my case).

systemctl enable zfs.target zfs-import-cache \
  zfs-mount zfs-import.target gdm

The following are some common tasks like setting the correct timezone, locale, hostname, creating a new user and enable the use of sudo.

# Set time and timezone
ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime # Change according to location…
hwclock --systohc # Sync with HW clock

# Configure locales
echo -e '
de_DE.UTF-8 UTF-8
en_US.UTF-8 UTF-8' >> /etc/locale.gen
echo 'KEYMAP=de-latin1' > /etc/vconsole.conf
echo 'LANG=de_DE.UTF-8' > /etc/locale.conf
locale-gen

# Set hostname
echo myhostname > /etc/hostname
echo -e '127.0.0.1 localhost\n::1 localhost\n127.0.1.1 myhostname' >> /etc/hosts

groupadd sudo
useradd -m -G sudo <username>
EDITOR=nano visudo # uncomment sudo group
passwd <username>

You're done! Exit the chroot with exit, unmount all filesystems, export the ZFS pool and reboot into your new OS!

exit
# Back in the installer shell…
umount -R /mnt
zfs umount -a
zpool export -a

Changelog

  • Mar 23, 2021: Import and verify against ArchZFS keys in final installation
  • Jul 26, 2021: Set the bootfs separately after creating the pool and root dataset, as it doesn't seem to work (anymore?) during zpool creation.
  • Jul 27, 2021: Remove NetworkManager as required service (you can still install and enable it if you want to use it)
Timo's Blog