diff options
| author | Craig Jennings <c@cjennings.net> | 2026-01-22 23:21:18 -0600 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-01-22 23:21:18 -0600 |
| commit | 0ffe7a85a1b024b88e4ddc3305c5f805edd6e8e1 (patch) | |
| tree | ccd6c610630cce9eef268ab692999cdfe3bb5a1b /docs/research-sandreas-zarch.org | |
| parent | 197a8036af21232276cfbd9624d9eeeebe722df6 (diff) | |
| download | archangel-0ffe7a85a1b024b88e4ddc3305c5f805edd6e8e1.tar.gz archangel-0ffe7a85a1b024b88e4ddc3305c5f805edd6e8e1.zip | |
Replace GRUB with ZFSBootMenu bootloader
This is a major change that replaces the GRUB bootloader with ZFSBootMenu,
providing native ZFS boot environment support.
Key changes:
- EFI partition reduced from 1GB to 512MB (only holds ZFSBootMenu)
- EFI now mounts at /efi instead of /boot
- Kernel and initramfs live on ZFS root (enables snapshot boot with matching kernel)
- Downloads pre-built ZFSBootMenu EFI binary from get.zfsbootmenu.org
- Creates EFI boot entries for all disks in multi-disk configurations
- Syncs ZFSBootMenu to all EFI partitions for redundancy
- Sets org.zfsbootmenu:commandline on zroot/ROOT for kernel cmdline inheritance
- Sets bootfs pool property for default boot environment
- AMD GPU workarounds (pg_mask, cwsr_enable) added to kernel cmdline when AMD detected
Deleted GRUB snapshot tooling (no longer needed):
- custom/grub-zfs-snap
- custom/40_zfs_snapshots
- custom/zz-grub-zfs-snap.hook
- custom/zfs-snap-prune
Updated helper scripts:
- zfssnapshot: removed grub-zfs-snap call, shows ZFSBootMenu tip
- zfsrollback: removed grub-zfs-snap call, notes auto-detection
Tested configurations:
- Single disk installation
- 2-disk mirror (mirror-0)
- 3-disk RAIDZ1 (raidz1-0)
- All boot correctly with ZFSBootMenu
Diffstat (limited to 'docs/research-sandreas-zarch.org')
| -rw-r--r-- | docs/research-sandreas-zarch.org | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/docs/research-sandreas-zarch.org b/docs/research-sandreas-zarch.org new file mode 100644 index 0000000..55bc77b --- /dev/null +++ b/docs/research-sandreas-zarch.org @@ -0,0 +1,365 @@ +#+TITLE: Research: sandreas/zarch ZFSBootMenu Installation +#+DATE: 2026-01-22 +#+AUTHOR: Research Notes + +* Overview + +This document summarizes research on the [[https://github.com/sandreas/zarch][sandreas/zarch]] GitHub repository for +Arch Linux ZFS installation. The project uses ZFSBootMenu, native encryption, +and automatic snapshots via zrepl. + +* Project Philosophy + +sandreas/zarch is described as a "single, non-modular file with some minor +config profiles" - the author explicitly avoids a "modular multi-script beast." +This contrasts with our more modular approach but offers useful patterns. + +** Key Features +- ZFSBootMenu as bootloader (not GRUB) +- Native ZFS encryption (AES-256-GCM) +- Automatic snapshots via zrepl +- EFI-only (no BIOS support) +- Profile-based configuration + +* ZFSBootMenu Installation + +** Download and Install +#+begin_src bash +# Create EFI directory +mkdir -p /efi/EFI/ZBM + +# Download latest ZFSBootMenu EFI binary +wget -c https://get.zfsbootmenu.org/latest.EFI -O /efi/EFI/ZBM/ZFSBOOTMENU.EFI + +# Or use curl variant +curl -o /boot/efi/EFI/ZBM/VMLINUZ.EFI -L https://get.zfsbootmenu.org/efi +#+end_src + +** EFI Boot Entry Registration +#+begin_src bash +efibootmgr --disk $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 +#+end_src + +** Key ZFSBootMenu Parameters +| Parameter | Purpose | +|------------------------+------------------------------------------------| +| zbm.timeout=N | Seconds to wait before auto-booting default | +| zbm.prefer=POOL | Preferred pool for default boot environment | +| zbm.import_policy | Pool import strategy (hostid recommended) | +| zbm.skip | Skip menu and boot default immediately | +| zbm.show | Force menu display | +| spl_hostid=0xXXXXXXXX | Host ID for pool import validation | + +** Kernel Command Line for Boot Environments +#+begin_src bash +# Set inherited command line on ROOT dataset +zfs set org.zfsbootmenu:commandline="quiet loglevel=0" zroot/ROOT + +# Set pool bootfs property +zpool set bootfs=zroot/ROOT/arch zroot +#+end_src + +* Dataset Layout + +** zarch Dataset Structure +#+begin_example +$POOL mountpoint=none +$POOL/ROOT mountpoint=none (container for boot environments) +$POOL/ROOT/arch mountpoint=/, canmount=noauto (active root) +$POOL/home mountpoint=/home (shared across boot environments) +#+end_example + +** Comparison: Our archzfs Dataset Structure +#+begin_example +zroot mountpoint=none, canmount=off +zroot/ROOT mountpoint=none, canmount=off +zroot/ROOT/default mountpoint=/, canmount=noauto, reservation=5-20G +zroot/home mountpoint=/home +zroot/home/root mountpoint=/root +zroot/media mountpoint=/media, compression=off +zroot/vms mountpoint=/vms, recordsize=64K +zroot/var mountpoint=/var, canmount=off +zroot/var/log mountpoint=/var/log +zroot/var/cache mountpoint=/var/cache +zroot/var/lib mountpoint=/var/lib, canmount=off +zroot/var/lib/pacman mountpoint=/var/lib/pacman +zroot/var/lib/docker mountpoint=/var/lib/docker +zroot/var/tmp mountpoint=/var/tmp, auto-snapshot=false +zroot/tmp mountpoint=/tmp, auto-snapshot=false +#+end_example + +** Key Differences +- zarch: Minimal dataset layout (ROOT, home) +- archzfs: Fine-grained datasets with workload-specific tuning +- archzfs: Separate /var/log, /var/cache, /var/lib/docker +- archzfs: recordsize=64K for VM storage +- archzfs: compression=off for media (already compressed) + +* ZFS Pool Creation + +** zarch Pool Creation (with encryption) +#+begin_src bash +zpool create -f \ + -o ashift=12 \ + -O compression=lz4 \ + -O acltype=posixacl \ + -O xattr=sa \ + -O relatime=off \ + -O atime=off \ + -O encryption=aes-256-gcm \ + -O keylocation=prompt \ + -O keyformat=passphrase \ + -o autotrim=on \ + -m none \ + $POOL ${DISK}-part2 +#+end_src + +** Our archzfs Pool Creation (with encryption) +#+begin_src bash +zpool create -f \ + -o ashift="$ASHIFT" \ + -o autotrim=on \ + -O acltype=posixacl \ + -O atime=off \ + -O canmount=off \ + -O compression="$COMPRESSION" \ + -O dnodesize=auto \ + -O normalization=formD \ + -O relatime=on \ + -O xattr=sa \ + -O encryption=aes-256-gcm \ + -O keyformat=passphrase \ + -O keylocation=prompt \ + -O mountpoint=none \ + -R /mnt \ + "$POOL_NAME" $pool_config +#+end_src + +** Key Differences +| Option | zarch | archzfs | Notes | +|-----------------+-------------------+-----------------------+---------------------------------| +| compression | lz4 | zstd (configurable) | zstd better ratio, more CPU | +| atime | off | off | Same | +| relatime | off | on | archzfs uses relatime instead | +| dnodesize | (default) | auto | Better extended attribute perf | +| normalization | (default) | formD | Unicode consistency | + +* Snapshot Automation + +** zarch: zrepl Configuration + +zarch uses zrepl for automated snapshots with this retention grid: + +#+begin_example +1x1h(keep=4) | 24x1h(keep=1) | 7x1d(keep=1) | 4x1w(keep=1) | 12x4w(keep=1) | 1x53w(keep=1) +#+end_example + +This means: +- Keep 4 snapshots within the last hour +- Keep 1 snapshot per hour for 24 hours +- Keep 1 snapshot per day for 7 days +- Keep 1 snapshot per week for 4 weeks +- Keep 1 snapshot per 4 weeks for 12 periods (48 weeks) +- Keep 1 snapshot per year + +#+begin_src yaml +# Example zrepl.yml structure +jobs: + - name: snapjob + type: snap + filesystems: + "zroot<": true + snapshotting: + type: periodic + interval: 15m + prefix: zrepl_ + pruning: + keep: + - type: grid + grid: 1x1h(keep=all) | 24x1h | 14x1d + regex: "^zrepl_.*" + - type: regex + negate: true + regex: "^zrepl_.*" +#+end_src + +** archzfs: Pacman Hook Approach + +Our approach uses pre-transaction snapshots: +#+begin_src bash +# /etc/pacman.d/hooks/zfs-snapshot.hook +[Trigger] +Operation = Upgrade +Operation = Install +Operation = Remove +Type = Package +Target = * + +[Action] +Description = Creating ZFS snapshot before pacman transaction... +When = PreTransaction +Exec = /usr/local/bin/zfs-pre-snapshot +#+end_src + +** Comparison: Snapshot Approaches +| Feature | zrepl (zarch) | Pacman Hook (archzfs) | +|-------------------+--------------------------+------------------------------| +| Trigger | Time-based (15 min) | Event-based (pacman) | +| Retention | Complex grid policy | Manual or sanoid | +| Granularity | High (frequent) | Package transaction focused | +| Recovery Point | ~15 minutes | Last package operation | +| Storage overhead | Higher (more snapshots) | Lower (fewer snapshots) | + +** Alternative: sanoid (mentioned in archzfs) +Sanoid provides similar functionality to zrepl with simpler configuration: +#+begin_src ini +# /etc/sanoid/sanoid.conf +[zroot/ROOT/default] +use_template = production +recursive = yes + +[template_production] +frequently = 0 +hourly = 24 +daily = 7 +weekly = 4 +monthly = 12 +yearly = 1 +autosnap = yes +autoprune = yes +#+end_src + +* EFI and Boot Partition Strategy + +** zarch: 512MB EFI, ZFSBootMenu +- Single 512MB EFI partition (type EF00) +- ZFSBootMenu EFI binary downloaded from upstream +- No GRUB, no separate boot partition on ZFS +- Kernel/initramfs stored on ZFS root (ZFSBootMenu reads them) + +** archzfs: 1GB EFI, GRUB with ZFS Support +- 1GB EFI partition per disk +- GRUB with ZFS module for pool access +- Redundant EFI partitions synced via rsync +- Boot files in EFI partition (not ZFS) + +** Trade-offs + +| Aspect | ZFSBootMenu | GRUB + ZFS | +|---------------------+--------------------------------+------------------------------| +| Boot environment | Native (designed for ZFS) | Requires ZFS module | +| Snapshot booting | Built-in, interactive | Custom GRUB menu entries | +| Encryption | Prompts for key automatically | More complex setup | +| EFI space needed | Minimal (~512MB) | Larger (kernel/initramfs) | +| Complexity | Simpler (single binary) | More moving parts | +| Recovery | Can browse/rollback at boot | Requires grub.cfg regen | + +* Pacman Hooks and Systemd Services + +** zarch Services +#+begin_example +zfs-import-cache +zfs-import.target +zfs-mount +zfs-zed +zfs.target +set-locale-once.service (custom first-boot locale config) +#+end_example + +** archzfs Services +#+begin_example +zfs.target +zfs-import-scan.service (instead of cache-based) +zfs-mount.service +zfs-import.target +NetworkManager +avahi-daemon +sshd +#+end_example + +** Key Difference: Import Method +- zarch: Uses zfs-import-cache (requires cachefile) +- archzfs: Uses zfs-import-scan (scans with blkid, no cachefile needed) + +The scan method is simpler and more portable (works if moving disks between +systems). + +* mkinitcpio Configuration + +** zarch Approach +#+begin_src bash +sed -i '/^HOOKS=/s/block filesystems/block zfs filesystems/g' /etc/mkinitcpio.conf +#+end_src + +** archzfs Approach +#+begin_src bash +HOOKS=(base udev microcode modconf kms keyboard keymap consolefont block zfs filesystems) +#+end_src + +** Important Notes +- Both use busybox-based udev (not systemd hook) +- archzfs explicitly removes autodetect to ensure all storage drivers included +- archzfs removes fsck (ZFS doesn't use it) +- archzfs includes microcode early loading + +* Useful Patterns to Consider + +** 1. Profile-Based Configuration +zarch uses a profile directory system: +#+begin_example +default/ + archpkg.txt # Official packages + aurpkg.txt # AUR packages + services.txt # Services to enable + zarch.conf # Core configuration + custom-chroot.sh # Custom post-install +#+end_example + +This allows maintaining multiple configurations (desktop, server, VM) cleanly. + +** 2. ZFSBootMenu for Simpler Boot +For future consideration: +- Native ZFS boot environment support +- Interactive snapshot selection at boot +- Simpler encryption key handling +- Smaller EFI partition needs + +** 3. zrepl for Time-Based Snapshots +For systems needing frequent snapshots beyond pacman transactions: +- 15-minute intervals for development machines +- Complex retention policies +- Replication to remote systems + +** 4. AUR Helper Installation Pattern +#+begin_src bash +# Build yay as regular user, install as root +su -c "git clone https://aur.archlinux.org/yay-bin.git" "$USER_NAME" +arch-chroot -u "$USER_NAME" /mnt makepkg -D /home/$USER_NAME/yay-bin -s +pacman -U --noconfirm yay-bin-*.pkg.tar.* +#+end_src + +* References + +- [[https://github.com/sandreas/zarch][sandreas/zarch GitHub Repository]] +- [[https://zfsbootmenu.org/][ZFSBootMenu Official Site]] +- [[https://docs.zfsbootmenu.org/en/latest/][ZFSBootMenu Documentation]] +- [[https://zrepl.github.io/][zrepl Documentation]] +- [[https://wiki.archlinux.org/title/ZFS][Arch Wiki: ZFS]] +- [[https://github.com/acrion/zfs-autosnap][zfs-autosnap - Pre-upgrade Snapshots]] +- [[https://aur.archlinux.org/packages/pacman-zfs-hook][pacman-zfs-hook AUR Package]] +- [[https://florianesser.ch/posts/20220714-arch-install-zbm/][Guide: Install Arch Linux on encrypted zpool with ZFSBootMenu]] + +* Action Items for archzfs + +Based on this research, potential improvements: + +1. [ ] Consider adding ZFSBootMenu as alternative bootloader option +2. [ ] Evaluate zrepl for systems needing frequent time-based snapshots +3. [ ] Document the grub-zfs-snap vs ZFSBootMenu trade-offs +4. [ ] Consider profile-based configuration for different use cases +5. [ ] Add sanoid configuration to archsetup for automated snapshot retention |
