Installation steps for running Arch Linux with root on ZFS using UEFI and systemd-boot
. All steps are run as root
.
- Set a bigger font if needed:
setfont latarcyrheb-sun32
- To connect to the internet add the ESSID and passphrase:
wpa_passphrase ESSID PASSPHRASE > /etc/wpa_supplicant/wpa_supplicant.conf
- Start wpa_supplicant and get an IP address:
wpa_supplicant -B -c /etc/wpa_supplicant/wpa_supplicant.conf -i <wifi interface>
dhclient <wifi interface>
- Wipe disks, create boot, swap and ZFS partitions:
sgdisk --zap-all /dev/disk/by-id/<disk0>
sgdisk -n1:0:+512M -t1:ef00 /dev/disk/by-id/<disk0>
sgdisk -n2:1052672:+8G -t2:8200 /dev/disk/by-id/<disk0>
sgdisk -n3:17829888:0 -t3:bf00 /dev/disk/by-id/<disk0>
sgdisk --zap-all /dev/disk/by-id/<disk1>
sgdisk -n1:0:+512M -t1:ef00 /dev/disk/by-id/<disk1>
sgdisk -n2:1052672:+8G -t2:8200 /dev/disk/by-id/<disk1>
sgdisk -n3:17829888:0 -t3:bf00 /dev/disk/by-id/<disk1>
- Format boot and swap partitions
mkfs.vfat /dev/disk/by-id/<disk0>-part1
mkfs.vfat /dev/disk/by-id/<disk1>-part1
mkswap /dev/disk/by-id/<disk0>-part2
mkswap /dev/disk/by-id/<disk1>-part2
swapon /dev/disk/by-id/<disk0>-part2 /dev/disk/by-id/<disk1>-part2
- On choosing
ashift
You should specify an ashift when that value is too low for what you actually need, either today (disk lies) or into the future (replacement disks will be AF). Looks like a sound advice to me. If in doubt, clarify before going any further. I will go with the defaults here, which will trigger autodetection.
Disk block size check:
cat /sys/class/block/<disk>/queue/{phys,log}ical_block_size
- Create pool (here's for a RAID 0 equivalent):
zpool create \
-O atime=off \
-O acltype=posixacl -O canmount=off -O compression=lz4 \
-O dnodesize=legacy -O normalization=formD \
-O xattr=sa -O devices=off -O mountpoint=none -R /mnt rpool /dev/disk/by-id/<disk0>-part3 /dev/disk/by-id/<disk1>-part3
If the pool is larger than 10 disks you should identify them by-path
or by-vdev
(see here for more details).
Check ashift
with:
zdb -C | grep ashift
- Create datasets:
zfs create -o canmount=off -o mountpoint=none rpool/ROOT
zfs create -o mountpoint=/ -o canmount=noauto rpool/ROOT/default
zfs create -o mountpoint=none rpool/DATA
zfs create -o mountpoint=/home rpool/DATA/home
zfs create -o mountpoint=/root rpool/DATA/home/root
zfs create -o mountpoint=/local rpool/DATA/local
zfs create -o mountpoint=none rpool/DATA/var
zfs create -o mountpoint=/var/log rpool/DATA/var/log # after a rollback, systemd-journal blocks at reboot without this dataset
zpool set bootfs=rpool/ROOT/default rpool
- Create swap (not needed if you have dedicated partitions, like above)
zfs create -V 16G -b $(getconf PAGESIZE) -o compression=zle -o logbias=throughput -o sync=always -o primarycache=metadata -o secondarycache=none -o com.sun:auto-snapshot=false rpool/swap
mkswap /dev/zvol/rpool/swap
- Unmount all
zfs umount -a
rm -rf /mnt/*
- Export pool:
zpool export rpool
- Re import it:
zpool import -d /dev/disk/by-id -R /mnt rpool -N
- Mount root, then the other datasets:
zfs mount rpool/ROOT/default
zfs mount -a
- Mount boot partition:
mkdir /mnt/boot
mount /dev/disk/by-id/<disk0>-part1 /mnt/boot
- Generate fstab:
mkdir /mnt/etc
genfstab -U /mnt >> /mnt/etc/fstab
- Add swap (not needed if you created swap partitions, like above):
echo "/dev/zvol/rpool/swap none swap discard 0 0" >> /mnt/etc/fstab
- Install the base system:
pacstrap /mnt base base-devel linux linux-firmware vim
If it fails, add GPG keys (see bellow).
- Change root into the new system:
arch-chroot /mnt
-
Remove all lines in
/etc/fstab
, leaving only the entries forswap
andboot
. -
Add ZFS repository in
/etc/pacman.conf
:
[archzfs]
Server = http://archzfs.com/$repo/x86_64
- Add GPG keys:
curl -O https://archzfs.com/archzfs.gpg
pacman-key -a archzfs.gpg
pacman-key --lsign-key DDF7DB817396A49B2A2723F7403BD972F75D9D76
pacman -Syy
- Configure time zone (change accordingly):
ln -sf /usr/share/zoneinfo/Region/City /etc/localtime
hwclock --systohc
- Generate locale (change accordingly):
sed -i 's/#\(en_US\.UTF-8\)/\1/' /etc/locale.gen
locale-gen
echo "LANG=en_US.UTF-8" > /etc/locale.conf
- Configure vconsole, hostname, hosts:
echo -e "KEYMAP=us\n#FONT=latarcyrheb-sun32" > /etc/vconsole.conf
echo al-zfs > /etc/hostname
echo -e "127.0.0.1 localhost\n::1 localhost" >> /etc/hosts
-
Set root password
-
Install ZFS, microcode etc:
pacman -Syu archzfs-linux amd-ucode networkmanager sudo openssh rsync borg
I choose the default options for the archzfs-linux group: zfs-linux
, zfs-utils
, and mkinitcpio
for initramfs.
- Generate host id:
zgenhostid $(hostid)
- Create cache file:
zpool set cachefile=/etc/zfs/zpool.cache rpool
- Configure initial ramdisk in
/etc/mkinitcpio.conf
:
HOOKS=(base udev autodetect modconf block keyboard zfs filesystems)
and regenerate it:
mkinitcpio -p linux
- Enable ZFS services:
systemctl enable zfs.target
systemctl enable zfs-import-cache.service
systemctl enable zfs-mount.service
systemctl enable zfs-import.target
- Install the bootloader:
bootctl --path=/boot install
- Add an EFI boot manager update hook in /etc/pacman.d/hooks/100-systemd-boot.hook:
[Trigger]
Type = Package
Operation = Upgrade
Target = systemd
[Action]
Description = update systemd-boot
When = PostTransaction
Exec = /usr/bin/bootctl update
- Replace content of /boot/loader/loader.conf with:
default arch
timeout 3
# bigger boot menu on a 4K display
#console-mode 1
- Create a /boot/loader/entries/arch.conf containing:
title Arch Linux
linux /vmlinuz-linux
initrd /amd-ucode.img
initrd /initramfs-linux.img
options zfs=rpool/ROOT/default rw
- Exit and unmount all:
exit
zfs umount -a
umount -R /mnt
- Export pool:
zpool export rpool
- Reboot
A minimal Arch Linux system with root on ZFS should now be configured.
- Create user
zfs create rpool/DATA/home/user
groupadd -g 1234 group
useradd -g group -u 1234 -d /home/user -s /bin/bash user
cp /etc/skel/.bash* /home/user
chown -R user:group /home/user && chmod 700 /home/user
- Create non-root pools:
zpool create \
-O atime=off \
-O acltype=posixacl -O canmount=off -O compression=lz4 \
-O dnodesize=auto \
-O xattr=sa -O devices=off -O mountpoint=none pool:a /dev/disk/by-id/<disk2> /dev/disk/by-id/<disk3>
zpool create \
-O atime=off \
-O acltype=posixacl -O canmount=off -O compression=lz4 \
-O dnodesize=auto \
-O xattr=sa -O devices=off -O mountpoint=none pool:b mirror /dev/disk/by-id/<disk4> /dev/disk/by-id/<disk5> mirror /dev/disk/by-id/<disk6> /dev/disk/by-id/<disk7>
- Create non-root datasets:
zfs create -o canmount=off -o mountpoint=none pool:a/DATA
zfs create -o mountpoint=/path pool:a/DATA/path
zfs create -o mountpoint=/path/games -o recordsize=1M pool:a/DATA/path/games
zfs create -o mountpoint=/path/transmission -o recordsize=1M pool:a/DATA/path/transmission
zfs create -o mountpoint=/path/backup -o compression=off pool:a/DATA/path/backup
- Create NFS share:
Set Domain in idmapd.conf
on server and clients.
zfs set [email protected]/24 pool:a/DATA/path/name
systemctl enable nfs-server.service zfs-share.service --now
- https://wiki.archlinux.org/index.php/Install_Arch_Linux_on_ZFS
- https://wiki.archlinux.org/index.php/ZFS
- https://ramsdenj.com/2016/06/23/arch-linux-on-zfs-part-2-installation.html
- https://github.com/reconquest/archiso-zfs
- https://zedenv.readthedocs.io/en/latest/setup.html
- https://docs.oracle.com/cd/E37838_01/html/E60980/index.html
- https://ramsdenj.com/2020/03/18/zectl-zfs-boot-environment-manager-for-linux.html
- https://superuser.com/questions/1310927/what-is-the-absolute-minimum-size-a-uefi-partition-can-be, https://systemd.io/9OOT_LOADER_SPECIFICATION/
- OpenZFS Admin Documentation
- zfs(8)
- zpool(8)
- https://jrs-s.net/category/open-source/zfs/