Install Arch Linux with ZFS root filesystem, zfs-dkms, ZFSBootMenu, Pacman Auto-snapshots, Secure Boot enabled
Disable Secure Boot. ZFS modules can not be loaded if Secure Boot is enabled.
Before moving on I need to point out that there exists a script that can automate the configuration and install of a ZFS root system. However as convenient as it sounds the script is limited in flexibility and scope. These limitation cannot be overcome unless one has the time and compacity to edit the script to their liking. If you want to install a ZFS root system as quickly as possible and don't care about any particulars than take a good look at this github page.
It's entirely possible to build your own Archiso however that takes time and beyond the scope of this guide. For now we'll take a shortcut.
By the same developer hosting the prebuilt ZFS supported install media above, the purpose of this script is to skip that step entirely and build the necessary zfs modules within the within the Arch install enviroment.
From the github page, boot on any archiso system, and run:
curl -s https://raw.githubusercontent.com/eoli3n/archiso-zfs/master/init | bash
Optionally after booting the Arch Install media and entering the install enviroment, set the console font:
>setfont ter-128n
ip a ping yahoo.com
reflector --country Japan --latest 5 --sort rate --save /etc/pacman.d/mirrorlist
>lsmod | grep zfs zfs 4218880 11 zunicode 339968 1 zfs zzstd 552960 1 zfs zlua 208896 1 zfs zavl 16384 1 zfs icp 331776 1 zfs zcommon 110592 2 zfs,icp znvpair 118784 2 zfs,zcommon spl 122880 6 zfs,icp,zzstd,znvpair,zcommon,zavl
We'll be using the UEFI boot method which requires us to create a EFI partition to launch Linux. Run the following command to identify the hard drive \ partition info:
>lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT mmcblk0 179:0 0 29.1G 0 disk nvme0n1 259:0 0 465.8G 0 disk
To make things simple in the above example we'll be using disk "nvme0n1" to hold both the EFI and OS filesystems.
Note: From a security standpoint it is entirely possible to have the EFI (boot) partition on a entirely different disk then the actual OS filesystem, eg: removable media mmcblk0. Look further down for the appropiate commands.
The file with the long string is our target disk (nvme0n1). Save the disk path\UUID into a variable to make things easy:
>DISK=/dev/nvme0n1
You can use almost any partitioning tool... in this example we'll use gdisk to create the partition scheme.
>sgdisk --zap-all $DISK >sgdisk -n1:1M:+256M -t1:EF00 $DISK >sgdisk -n2:0:0 -t2:BF00 $DISK
Warning: If you're using a Libvirt \ Qemu virtual machine the above commands may fail with an obscure error. If this is the case try using gdisk in interactive mode to manually create the partition scheme. Don't forget the label the partitions correctly: EFI Boot Partition=ef00, Linux Install Partition=bf00
>gdisk /dev/vda
Now lets verify the partitions are good before moving on:
>lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS mmcblk0 179:0 0 29.1G 0 disk nvme0n1 259:0 0 465.8G 0 disk ├─nvme0n1p1 259:1 0 256M 0 part └─nvme0n1p2 259:2 0 465.5G 0 part
If you're putting the EFI partition on a seperate disk \ removeable storage device for additional security eg., mmcblk0p1.
>DISK0=/dev/mmcblk0 >DISK1=/dev/nvme0n1 >sgdisk --zap-all $DISK0 >sgdisk --zap-all $DISK1 >sgdisk -n1:1M:+256M -t1:EF00 $DISK0 >sgdisk -n2:0:0 -t2:BF00 $DISK1 >lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS mmcblk0 179:0 0 29.7G 0 disk └─mmcblk0p1 179:1 0 512M 0 part nvme0n1 259:0 0 238.5G 0 disk └─nvme0n1p1 259:1 0 238.5G 0 part
Create the EFI (boot) filesystem on the first partition:
>mkfs.vfat -v -F 32 -n EFI /dev/nvme0n1p1
Or if you're going the seperate disk \ removeable storage device route:
>mkfs.vfat -v -F 32 -n EFI /dev/mmcblk0p1
Create our zroot pool.
Note: The lowercase and capital o's matter in the command, if you're not using an SSD drive skip the autotrim option, and if you're using a virtual enviroment you can probably omit the compression option as well.
>zpool create -f -o ashift=12 \ -o autotrim=on \ -O devices=off \ -O relatime=on \ -O xattr=sa \ -O acltype=posixacl \ -O denodesize=legacy \ -O normalization=formD \ -O compression=lz4 \ -O canmount=off \ -O mountpoint=none \ -R /mnt \ zroot /dev/nvme0n1p2
Verify the pool:
>zpool status
Create filesystem mountpoints and then import \ export test:
>zfs create zroot/ROOT >zfs create -o canmount=noauto -o mountpoint=/ zroot/ROOT/arch >zfs create -o mountpoint=/home zroot/home >zpool export zroot >zpool import -d /dev/nvme0n1p2 -R /mnt zroot -N
Mount our mountpoints:
>zfs mount zroot/ROOT/arch >zfs mount -a >mkdir -p /mnt/{etc/zfs,boot/efi} >mount /dev/nvme0n1p1 /mnt/boot/efi
Check if zfs mounted successfully:
>mount | grep mnt zroot/ROOT/arch on /mnt type zfs (rw,relatime,xattr,posixacl) zroot/home on /mnt/home type zfs (rw,relatime,xattr,posixacl)
Make sure the df output shows all three mountpoints:
>df -k zroot/ROOT/arch... /mnt zroot/home... /mnt/home /dev/nvme0n1p1... /boot/efi
If all is good, move on to set bootfs and create zfs cache file:
>zpool set bootfs=zroot/ROOT/arch zroot >zpool set cachefile=/etc/zfs/zpool.cache zroot >cp -v /etc/zfs/zpool.cache /mnt/etc/zfs
Install packages with pacstrap:
>pacman -Syy >pacstrap /mnt base base-devel linux linux-headers linux-firmware intel-ucode dkms efibootmgr man-db man-pages git rust cargo nano
Generate and configure the filesystem table file.
With nano comment out or delete all lines but the one containing /efi.
>genfstab -U -p /mnt >> /mnt/etc/fstab >nano /mnt/etc/fstab
Copy over the dns settings to the new system:
>cp -v /etc/resolv.conf /mnt/etc
In makepkg.conf on the "CFLAGS" line, remove any -march and -mtune flags, then add -march=native. Scroll down to the line with MAKEFLAGS="-j2" and change that to MAKEFLAGS="-j$(nproc)".Near the bottom of the file look for the file compression options, add "--threads=0" to the COMPRESSZST and COMPRESSXZ commands.
There are further tweaks to be had within this file, please consult the Archlinux wiki below.
>arch-chroot /mnt >nano /etc/makepkg.conf
From the Archlinux wiki Eg:
/etc/makepkg.conf ... CFLAGS="-march=native -O2 -pipe -fno-plt" ... RUSTFLAGS="-C opt-level=2 -C target-cpu=native" ... MAKEFLAGS="-j$(nproc)" ... COMPRESSZST=(zstd -c -z -q --threads=0 -) COMPRESSXZ=(xz -c -z --threads=0 -) ...
>useradd -m username >passwd username >usermod -aG users, sys, adm, log, scanner, power, rfkill, video, storage, optical, lp, audio, wheel username >id username
Add "%wheel ALL=(ALL) ALL" without quotes using nano:
>nano /etc/sudoers.d/username
>su username >sudo pacman -Syy >git clone https://aur.archlinux.org/paru.git && cd paru >makepkg -si
>cd >paru -S zfs-dkms
This is about the bare minimal packages needed. You can even omit openssh if you don't plan on doing any remote management and terminus-font. Note: You can use dhcpcd in place of networkmanager for a less system resource alternative.
>paru -S networkmanager reflector openssh terminus-font
paru -S pacman-static
In addition, for a more complete Desktop or Laptop experience I recommend install the following packages.
Note: I placed a star next to packages that will require some manual intervention to get working. Look up the package in the Archlinux Wiki for guidance.
xdg-user-dirs xdg-utils bash-completion gpm* tmux terminus-font inetutils net-tools dnsutils avahi* nss-mdns firewalld* tlp* acpi_call acpid* bluez* bluez-utils cups* ntp* alsa-utils pipewire pipewire-alsa pipewire-pulse pipewire-jack sof-firmware smartmontools* lm_sensors* curl wget lsftp rsync dmidecode lsof htop fcron zsh* grml-zsh-config fd fzf* exfatprogs ntfs-3g dosfstools neovim
>systemctl enable zfs-import-cache >systemctl enable zfs-import.target >systemctl enable zfs-mount >systemctl enable zfs-share >systemctl enable zfs-zed >systemctl enable zfs.target >systemctl enable NetworkManager >systemctl enable reflector.timer
>sudo zgenhostid $(hostid) >hostid
>sudo ln -sf /usr/share/zoneinfo/Pacific/Guam /etc/localtime >hwclock --systohc
Generate your locales, edit /etc/locale.gen and uncomment all locales you need, for example en_US.UTF-8.
>nano /etc/locale.gen >locale-gen
Set your hostname by writing it to /etc/hostname:
>echo "hostname" > /etc/hostname
Then edit /etc/hosts, where is your previously chosen hostname:
>echo "127.0.0.1 localhost" >> /etc/hosts >echo "::1 localhost" >> /etc/hosts >echo "127.0.0.1 hostname.localdomain hostname" >> /etc/hosts
Edit the reflector configuration file by adding the correct country and changing "--sort rate".
>nano /etc/xdg/reflector.conf
This will make your console look cleaner however you're welcome to customize to you're liking by using another font or changing its size.
>sudo nano /etc/vconsole.conf FONT=ter-128n
>mkdir -p /efi/EFI/zbm >wget https://get.zfsbootmenu.org/latest.EFI -O /efi/EFI/zbm/zfsbootmenu.EFI >efibootmgr --disk --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
zfs set org.zfsbootmenu:commandline="noresume init_on_alloc=0 rw spl.spl_hostid=$(hostid)" zroot/ROOT
Edit the mkinitcpio configuration file and make sure the HOOKS line are like the following:
"HOOKS=(base udev autodetect modconf block keyboard zfs filesystem)"
>nano /mnt/etc/mkinitcpio.conf >mkinitcpio -P
>passwd
>umount /mnt/boot/efi >zfs umount -a >zpool export zroot >reboot
One of the main features of the ZFS filesystem is its ability to take filesystem snapshots. This feature quite invalueable especially during major Operating System upgrades and software updates. However initiating a manual ZFS snapshot everytime something changes on the system can be very tedious. Using a Pacman hook is a very effective way to solve this issue.
- https://aur.archlinux.org/packages/pacman-zfs-hook
- https://github.com/RileyInkTheCat/Pacman-ZFS-Hook
The hook calls on the "zfs-snap-pac" script to create a snapshot whenever there is a pacman transaction. You can customize this script to your liking. For example we'll modify the date command within the GetSnapshotName() function so that it names snapshots in a more meaningful manner.
... GetSnapshotName() { local time=$(date +%s) snapshotName="$snapshotName$time" } ...
Example output of default snapshot naming scheme:
>date +%s 1708100748
New human friendly naming scheme:
>date +%F-%R 2024-02-17-02:25
Creating snapshots everytime there's a Pacman transaction can quickly take up valuable space if old snapshots are never deleted. Use another hook to automate this task and avoid future potential issues.
- https://aur.archlinux.org/packages/zfs-prune-snapshots
- https://github.com/bahamas10/zfs-prune-snapshots
Use the hook file below to automatically call the zfs-prune-snapshots script after every Pacman transaction has completed. The arguement "2w" will instruct the script to delete any ZFS snapshot matching 2 weeks or older.
>cat /usr/share/libalpm/hooks/01-zfs-prune-pac.hook [Trigger] Operation = Upgrade Operation = Remove Type = Package Target = * [Action] Description = Pruning ZFS snapshots... When = PostTransaction Exec = /usr/bin/zfs-prune-snapshots 2w
To fix the "WARNING: Possible missing firmware" messages whenever regenerating the initramfs:
>paru -S mkinitcpio-firmware
If the system won't boot you can remount the zfs pool and EFI boot partition using the install media like before. After troubleshooting, don't forget to unmount it described in the previous step.
>lsblk >zpool import -f -N -R /mnt zroot >zfs list >zfs mount zroot/ROOT/arch_mpv-libplacebo2_NEW >ls /mnt >lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS sr0 11:0 1 883.3M 0 rom mmcblk0 179:0 0 29.7G 0 disk └─mmcblk0p1 179:1 0 512M 0 part /boot/efi zram0 253:0 0 2G 0 disk [SWAP] nvme0n1 259:0 0 238.5G 0 disk └─nvme0n1p1 259:1 0 238.5G 0 part >mount /dev/mmcblk0p1 /mnt/boot/efi >zfs mount zroot/home >mount | grep mnt >arch-chroot /mnt
If you're mounting using a different distro eg: Alpine Linux:
>zpool import -f zroot >mount -t zfs zroot/ROOT/alpine /mnt >mount -t zfs zroot/home /mnt/home >mount /dev/vda1 /mnt/boot/efi >mount | grep mnt >chroot /mnt /usr/bin/env sh
Reset the zfs pool and umount
>zpool export -f zroot >zfs umount -a
how to fix missing libcrypto.so.1.1?
/var stays busy at shutdown due to journald #867
Arch Linux, Aur error - FAILED unknown public key
zfs-dkms depends on a specific version of the zfs-utils, and zfs-utils depend on a specific version of zfs-dkms, which completely prevents me from updating them
2022: Arch Linux Root on ZFS from Scratch Tutorial
Guide: Install Arch Linux on an encrypted zpool with ZFSBootMenu as a bootloader
Debian Bullseye installation with ESP on the zpool disk
Setting up Arch + LUKS + BTRFS + systemd-boot + apparmor + Secure Boot + TPM 2.0 - A long, nightmarish journey, now simplified
Configure systemd ZFS mounts
The Archzfs unofficial user repository offers multiple ways to install the ZFS kernel module.
Arch Linux pacman hooks
Paru: Feature packed AUR helper
Please feel free to leave any helpful comments or suggestions
@timofeika There are some minor typo's here: