Linux · July 28, 2021 · 6 min read

Install Arch Linux on ZFS with Native ROOT Encryption

Full walkthrough for installing Arch Linux with ZFS as the root filesystem, native encryption via a passphrase, and GRUB bootloader configuration.

Prepare Installation Media

Grab the latest Arch ISO and write it to a USB.

Linux

Use dd. It’s simple:

sudo dd if=<Path to ISO> of=<Target Device> bs=4096 status=progress

Example:

sudo dd if=./archlinux-2021.07.01-x86_64.iso of=/dev/sdb bs=4096 status=progress

Windows

Use Etcher. Download it, run as administrator, pick your ISO and target drive. Done.

Boot from the newly created Arch install media.

Prepare Installation Environment

Set Mirrors

Once you’re in the live environment, rank the mirrors. This picks the fastest ones for you:

reflector --latest 20 --protocol https --sort rate --save /etc/pacman.d/mirrorlist

Then refresh the package database:

pacman -Syy

Load ZFS Modules

The live ISO doesn’t ship with ZFS modules, so we need to load them. eoli3n has a script that handles this automatically:

curl https://eoli3n.github.io/archzfs/init | bash

Configure Disks

Create Partitions

GRUB can’t boot from an encrypted boot partition, so we need a separate unencrypted /boot using something like ext4.

Set up a GPT partition table. Check the Arch Partitioning Guide if you need more detail. Your partitions should look roughly like this:

PartSizeType
1100MEFI boot partition (ef00)
2500MLinux Filesystem
3XXXGSolaris Root (bf00)

I’ll assume the disk is sda. So the EFI partition would be /dev/sda1.

Create ZFS Pool

A ZFS pool can span a single partition or multiple disks in RAID. Check the official docs for custom configs. We’re keeping it simple with a single disk.

zpool create -f -o ashift=12 \
  -O acltype=posixacl \
  -O relatime=on \
  -O xattr=sa \
  -O dnodesize=legacy \
  -O normalization=formD \
  -O mountpoint=none \
  -O canmount=off \
  -O devices=off \
  -R /mnt \
  -O compression=lz4 \
  -O encryption=aes-256-gcm \
  -O keyformat=passphrase \
  -O keylocation=prompt \
  zroot /dev/disk/by-id/id-to-disk

Create ZFS Datasets

Root datasets:

zfs create -o mountpoint=none zroot/data
zfs create -o mountpoint=none zroot/ROOT
zfs create -o mountpoint=/ -o canmount=noauto zroot/ROOT/default
zfs create -o mountpoint=/home zroot/data/home

System datasets. I’d recommend these:

zfs create -o mountpoint=/var -o canmount=off zroot/var
zfs create zroot/var/log
zfs create -o mountpoint=/var/lib -o canmount=off zroot/var/lib

Optional datasets for libvirt and Docker:

zfs create zroot/var/lib/libvirt
zfs create zroot/var/lib/docker

Export and Import Your Pool

Not strictly required, but it’s a good sanity check. Export and re-import to make sure everything is configured right:

zpool export <Pool Name>          # e.g. zpool export zroot
zpool import <Pool Name> -R /mnt  # e.g. zpool import zroot -R /mnt

Mount ZFS Datasets

Mount the root dataset first:

zfs mount <Pool Name>/ROOT/<ROOT Dataset>   # e.g. zfs mount zroot/ROOT/default

Then everything else:

zfs mount -a

Mount Boot and EFI

mount <Path to Boot partition> /mnt/boot
mount <Path to EFI Partition> /mnt/boot/efi

Pacstrap base

Bootstrap the base system:

pacstrap /mnt base base-devel

Generate fstab

genfstab -f /boot -U /mnt > /mnt/etc/fstab

No need to include ZFS datasets here. GRUB and the ZFS manager handle mounting those automatically.

Now chroot in:

arch-chroot /mnt

Install The Kernel

You’ve got three options:

Install whichever you prefer:

pacman -Syy <Kernel> <Kernel>-headers
# e.g. pacman -Syy linux-zen linux-zen-headers

You can install multiple kernels side by side if you want.

Install CPU Microcode

Install ZFS

Add the archzfs repo

Append this to /etc/pacman.conf:

[archzfs]
Server = https://archzfs.com/$repo/$arch

Add their signing keys:

pacman-key -r DDF7DB817396A49B2A2723F7403BD972F75D9D76
pacman-key --lsign-key DDF7DB817396A49B2A2723F7403BD972F75D9D76

Refresh:

pacman -Syy

Install the ZFS kernel module

pacman -S zfs-dkms

This compiles ZFS from source for all installed kernels, so it might take a few minutes.

Configure initramfs

Edit /etc/mkinitcpio.conf. Add ZFS to the HOOKS line. It should look like:

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

The keyboard hook goes before zfs so you can actually type your pool password at boot. We skip fsck since ZFS doesn’t need it.

Regenerate initramfs:

mkinitcpio -P

Configure systemd

Set a cache file for your pool:

zpool set cachefile=/etc/zfs/<Pool Name>.cache <Pool Name>

Enable the ZFS systemd services:

systemctl enable zfs-mount zfs-import-scan zfs-import-cache

Install the Bootloader

We’re using GRUB with UEFI:

pacman -S grub efibootmgr os-prober

Install GRUB to disk

ZPOOL_VDEV_NAME_PATH=1 grub-install --efi-directory=<EFI Mountpoint> \
  --bootloader-id=<Desired Name> --target=x86_64-efi
# e.g. ZPOOL_VDEV_NAME_PATH=1 grub-install --efi-directory=/boot/efi \
#   --bootloader-id="ArchZFS" --target=x86_64-efi

Generate GRUB config

ZPOOL_VDEV_NAME_PATH=1 grub-mkconfig -o /boot/grub/grub.cfg

GRUB assumes the kernel will be booted from the pool as root, but the path needs a small fix. Replace /ROOT with <Pool Name>/ROOT:

sed -i "s@/ROOT@<Pool Name>/ROOT@" /boot/grub/grub.cfg
# e.g. sed -i "s@/ROOT@zroot/ROOT@" /boot/grub/grub.cfg

You only need to do this once, or when you add a new kernel.

Set Timezone and Locale

Timezone

ln -s /usr/share/zoneinfo/<Your Timezone> /etc/localtime
hwclock --systohc
# e.g. ln -s /usr/share/zoneinfo/Asia/Kolkata /etc/localtime

Locale

Uncomment your locale in /etc/locale.gen (usually en_US.UTF-8), then:

locale-gen
echo "LANG=en_US.UTF-8" >> /etc/locale.conf

NTP

pacman -S ntp
systemctl enable ntpd

Text Editor

pacman -S nano

Networking

pacman -S networkmanager dhcpcd iwd
systemctl enable dhcpcd NetworkManager

Configure Users

Set root password:

passwd

Create your user:

useradd -m <user>
passwd <user>

If you skip the -m flag, fix the permissions manually:

chown <user>:<user> /home/<user>

Install a Desktop Environment (Optional)

The Arch wiki has a full guide. For a basic KDE setup:

pacman -S plasma pipewire pipewire-pulse sddm pipewire-media-session konsole

For Wayland:

pacman -S plasma-wayland-session

Unmount and Reboot

exit
umount /mnt/boot/efi
umount /mnt/boot
zpool export <Pool Name>   # e.g. zpool export zroot
reboot

You’ll be asked for your pool password on every boot.

You can also use shavee to encrypt home directories with a separate key.

Let me know if I missed anything.

← All posts
Category: Linux