Skip to content

Instantly share code, notes, and snippets.

@tomka
Forked from Soulsuke/arch_on_zfs.txt
Created April 16, 2024 18:27
Show Gist options
  • Save tomka/14356c7f7b746cf6fb8e95dc2340d426 to your computer and use it in GitHub Desktop.
Save tomka/14356c7f7b746cf6fb8e95dc2340d426 to your computer and use it in GitHub Desktop.
Arch on zfs root + native encryption at rest + ZfsBootMenu (UEFI without another bootloader)
NOTE: requires an EFI bios, so zfs will not use the whole disk as this is a single disk scenario.
/*\
|* Prerequisite: preare archiso with zfs support
\*****************************************************************************/
// 0. Do everything as root.
// 1. Install archiso
pacman -S archiso
// Copy releng profile:
mkdir archlive
cp -r /usr/share/archiso/configs/releng/* archlive
// Add in archzfs + zfs-linux repositories (above all the rest):
// NOTE: you can change the repo name from 'zfs-linux' to 'zfs-linux-zen' or 'zfs-linux-lts' depending on your needs.
vim archlive/pacman.conf
[archzfs]
SigLevel = Optional TrustAll
Server = http://archzfs.com/$repo/x86_64
Server = http://mirror.sum7.eu/archlinux/archzfs/$repo/x86_64
Server = https://mirror.biocrafting.net/archlinux/archzfs/$repo/x86_64
[zfs-linux]
Server = http://kernels.archzfs.com/$repo
// Add in zfs package (x86_64 only):
vim archlive/packages.x86_64
archzfs-linux
linux-headers
// Build the iso:
mkarchiso -v -w /tmp/archiso-tmp -o . archlive
// Cleanup:
rm -fr archlive /tmp/archiso-tmp
// Write to usb device:
dd if=archlinux-20XX.XX.XX-x86_64.iso of=/dev/sdX status=progress
/*\
|* Install Arch
\*****************************************************************************/
// Keymap:
loadkeys <keymap>
// Check if the system is actually running in EFI mode:
ls /sys/firmware/efi/efivars
// Connect to the internet
// Update system clock:
timedatectl set-ntp true
// Create a gpt partition on disk with an EFI partition and another one for the zpool:
// NOTE: using DISK variable for convenience, set the right dev in there.
DISK="/dev/sdX"
parted -s ${DISK} mklabel gpt
parted -sa optimal ${DISK} mkpart ESP fat32 1MiB 512MiB
parted -s ${DISK} set 1 esp on
parted -sa optimal ${DISK} mkpart primary ext4 512MiB 100%
// Get the last partition's partuuid:
blkid
// Create the zfs pool:
zpool create zroot /dev/disk/by-partuuid/PARTUUID-GOTTEN-WITH-BLKID
// Set zfs cache:
zpool set cachefile=/etc/zfs/zpool.cache zroot
// Zfs tuning:
zfs set relatime=on zroot
zfs set compression=zstd zroot
zfs set checksum=blake3 zroot
zfs set mountpoint=none zroot
// Create an encrypted partition:
zfs create -o encryption=on -o keyformat=passphrase -o mountpoint=none zroot/e
// Create root zvols:
zfs create \
-o mountpoint=none \
-o acltype=posixacl \
-o org.zfsbootmenu:commandline="rw zfs.zfs_arc_max=4294967296" \
-o canmount=noauto \
zroot/e/ROOT
zfs create -o mountpoint=/ zroot/e/ROOT/arch
// Create home zvol:
zfs create -o mountpoint=/home zroot/e/home
// Create swap zvol:
zfs create -V 2G -b $(getconf PAGESIZE) \
-o logbias=throughput \
-o sync=always \
-o primarycache=metadata \
-o secondarycache=none \
-o com.sun:auto-snapshot=false \
-o compression=zle \
zroot/e/swap
mkswap -f /dev/zvol/zroot/e/swap
swapon /dev/zvol/zroot/e/swap
// (OPTIONAL) Create tmp zvol, if you want /tmp to be persistent:
zfs create \
-o setuid=off \
-o devices=off \
-o sync=disabled \
-o mountpoint=/tmp \
zroot/tmp
systemctl mask tmp.mount
// (OPTIONAL) Create any other needed dataset.
// Umount all zfs volumes:
swapoff -a
zpool export zroot
// Import the pool where the arch chroot will take place:
mkdir x
zpool import -R /root/x zroot
// Mount encrypted datasets:
zfs load-key zroot/e
zfs mount -la
// Setup the EFI partition as well:
mkfs.fat -F32 /dev/sdX1
mkdir -p /root/x/boot/efi
mount /dev/sdX1 /root/x/boot/efi
// Install the base system:
pacstrap x base linux linux-firmware
// Copy the zpool cache file over:
mkdir x/etc/zfs
cp /etc/zfs/zpool.cache /root/x/etc/zfs/zpool.cache
// Add in fstab data:
genfstab -U /root/x >> /root/x/etc/fstab
// Chroot:
arch-chroot x
// Vim:
pacman -S vim
// Pacman key init:
pacman-key --init
pacman-key --populate archlinux
// Add in atchzfs key:
curl -L https://archzfs.com/archzfs.gpg | pacman-key -a -
pacman-key -r DDF7DB817396A49B2A2723F7403BD972F75D9D76
pacman-key --lsign-key DDF7DB817396A49B2A2723F7403BD972F75D9D76
// Add in archzfs + zfs-linux repositories (above all the rest):
// NOTE: you can change the repo name from 'zfs-linux' to 'zfs-linux-zen' or 'zfs-linux-lts' depending on your needs.
vim /etc/pacman.conf
[archzfs]
Include = /etc/pacman.d/mirrorlist-archzfs
[zfs-linux]
Server = http://kernels.archzfs.com/$repo
// Install zfs stuff:
pacman -Syu
pacman -S archzfs-linux
// Create a hostid file:
zgenhostid -f $(hostid)
// Enable zfs stuff:
zpool set cachefile=/etc/zfs/zpool.cache zroot
systemctl enable zfs.target
systemctl enable zfs-import-cache
systemctl enable zfs-mount
systemctl enable zfs-import.target
// Installation settings:
// NOTE: pick the timezone you want.
timedatectl set-timezone Europe/Rome
hwclock --systohc
vim /etc/locale.gen
enable stuff
locale-gen
vim /etc/vconsole.conf
KEYMAP=<keymap>
FONT=lat2-16
vim /etc/hostname
<hostname>
vim /etc/hosts
127.0.0.1 localhost
::1 localhost
127.0.1.1 <hostname>.localdomain <hostname>
passwd
// Fix fstab:
vim /etc/fstab
# Swap:
/dev/zvol/zroot/e/swap none swap discard 0 0
# EFI:
UUID=<EFI PARTITION UUID> /boot/efi vfat rw,relatime,fmask=0177,dmask=0077,iocharset=utf8,shortname=mixed,errors=remount-ro,noauto 0 2
// Prepare the EFI partition for ZfsBootMenu:
pacman -S efibootmgr
mkdir -p /boot/efi/EFI/zbm
// Download ZfsBootMenu (check here for the latest release: https://github.com/zbm-dev/zfsbootmenu/releases )
curl -L https://github.com/zbm-dev/zfsbootmenu/releases/download/v2.2.0/zfsbootmenu-release-x86_64-v2.2.0-vmlinuz.EFI -o /boot/efi/EFI/zbm/zfsbootmenu.EFI
// Add in the new efi menu entry:
// IMPORTANT:
// - check the value of --disk
// - check the value of --part
// - check the value of rd.vconsole.keymap
// - check the value of rd.vconsole.font (lat2-16 is fine for displays smaller than 4k)
// If this fails, your pc won't boot up.
efibootmgr --disk /dev/sdX --part 1 --create --label "ZFSBootMenu" --loader '\EFI\zbm\zfsbootmenu.EFI' --unicode "spl.spl_hostid=0x$(hostid) zbm.timeout=10 zbm.prefer=zroot zbm.import_policy=hostid rd.vconsole.keymap=<keymap> rd.vconsole.font=<font> quiet" --verbose
// Safely unmount the efi partition:
umount /boot/efi
// Disclaimer: I find adding in hooks calling bash code quite tedious to maintain, and in some cases it isn't easy to specify targets.
// For this reasons I've put up a repo oh bash scripts I use: https://github.com/Soulsuke/arch-zfs-tools
// If you do not care about setting up these scripts skip to ARCH-ZFS-TOOLS-END, but you may want to read up which hooks I'm setting up as you may want/need something like them.
// Clone the repository:
cd /opt
git clone https://github.com/Soulsuke/arch-zfs-tools
// Create the pacman hooks folder:
mkdir /etc/pacman.d/hooks
// Create a pacman hook to automatically take snapshots when the linux package gets updated:
// NOTE: change target if you're using a different kernel.
vim /etc/pacman.d/hooks/00-zfs-snapshotter_root.hook
[Trigger]
Type = Package
Operation = Install
Operation = Upgrade
Operation = Remove
Target = linux
[Trigger]
Type = Path
Operation = Install
Operation = Upgrade
Operation = Remove
Target = usr/lib/modules/*/vmlinuz
Target = usr/lib/initcpio/*
[Action]
Description = Creating a backup BE...
When = PreTransaction
Exec = /usr/bin/sh -c '/opt/arch-zfs-tools/zfs-snapshotter.bash zroot/e/ROOT/arch 3 "$(uname -r)"'
// ARCH-ZFS-TOOLS-END
// To avoid having to type the zfs password twice, create a passphrase file:
vim /etc/zfs/zroot.key
<your password in plain text, no extra characters or newline at the end!>
chmod 600 /etc/zfs/zroot.key
// Set the key file for the encrypted dataset:
zfs change-key -o keylocation=file:///etc/zfs/zroot.key -o keyformat=passphrase zroot/e
// NOTE: to manually unlock the pool without the keyfile (shall you need it) you'll have to use:
// zfs load-key -L passphrase zroot/e
// Otherwise load-key will fail as it will look for the keyfile.
// Set zfs hooks + hostid + zfs key in mkinitcpio.conf:
vim /etc/mkinitcpio.conf
FILES=(/etc/hostid /etc/zfs/zroot.key)
...
HOOKS=( ... autodetect microcode modconf kms keyboard keymap block zfs filesystems ... )
// IMPORTANT: usually bundling the key file in the initramfs is a BAD IDEA as the key becomes readable for anyone who can access the file.
// We can safely do it here ONLY BECAUSE the resulting initramfs file will be stored on an encrypted drive.
// Generate the initramfs:
mkinitcpio -P
// Install intel-ucode if needed or (amd-ucode), which will also run the hook to bundle it all together:
pacman -S intel-ucode
// Harden the system a little since the initramfs contains the key to unlock the pool:
chmod 700 /boot
chmod 600 /boot/*
chmod 700 /boot/efi
// Create a systemd service to perform zpool scrubs:
vim /etc/systemd/system/zpool-scrub@.service
[Unit]
Description=Zpool scrub
After=zfs-load-key.service
[Service]
ExecStart=/usr/bin/zpool scrub %i
// Create a systemd timer to launch scrubs periodically:
vim /etc/systemd/system/zpool-scrub@.timer
[Unit]
Description=Weekly zpool scrub
[Timer]
OnCalendar=Mon *-*-* 10:00:00
Persistent=true
Unit=zpool-scrub@%i.service
[Install]
WantedBy=timers.target
// Enable the periodic zpool scrub:
systemctl daemon-reload
systemctl enable zpool-scrub@zroot.timer
// Must have for the next reboot:
vim /etc/pacman.conf
[multilib]
Include = /etc/pacman.d/mirrorlist
pacman -Syu networkmanager
systemctl enable NetworkManager
// (OPTIONAL) Install some extra packages:
pacman -S zsh xorg lightdm lightdm-gtk-greeter base-devel ...
// Unmount everything before rebooting:
exit
umount /root/x/boot/efi
zpool export zroot
shutdown -h now
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment