aboutsummaryrefslogtreecommitdiff
path: root/docs/research-sandreas-zarch.org
blob: 55bc77b58ed48b662e6ed111a8d40975584ed9e5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
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