Extends the server installer design with a ZFS path: F8 on the disk picker switches from one UFS disk to a marked set of disks, which then get arranged into mirrors or raidz, given options, and reviewed before anything is erased.
ZFS is a branch off the existing disk picker, not a new installer. UFS stays the default and its path is unchanged: one disk, whole-disk install.
/private/var/vm, per the E14 plan. The only partitions are the EFI system partition and the filesystem.A filesystem line sits above the disk list. In UFS mode the picker behaves exactly as it does now: one disk, Enter to select.
┌──────────────────────── Select Install Disk ─────────────────────────┐ │ │ │ The selected disk will be ERASED in full. Pick a target: │ │ │ │ Filesystem: [ UFS ] ZFS F8 switch │ │ │ │ ▸ ada0 238 GB Samsung SSD 860 EVO │ │ ├─ "BACKUP" 212 GB ufs │ │ └─ "EFI" 260 MB fat32 (ESP) │ │ │ │ ada1 931 GB WDC WD10EZEX-08WN4A0 │ │ └─ "vault" 931 GB zfs (zpool: tank) │ │ │ │ ada2 238 GB Samsung SSD 860 EVO │ │ │ │ da0 15 GB SanDisk Ultra USB (install media) │ │ │ │ Enter selects · F8 switches to ZFS · da0 is locked (booted from) │ │ │ ├────────────────────────────────────────────────────────────────────────┤ │ < Select > < Back > < Rescan > │ └────────────────────────────────────────────────────────────────────────┘
F8 switches to ZFS mode. Every row gains a checkbox, Space marks or unmarks the highlighted disk, and Enter moves on to lay out the pool.
┌───────────────────────── Select Pool Disks ──────────────────────────┐ │ │ │ Mark every disk for the pool. Marked disks are ERASED. │ │ │ │ Filesystem: UFS [ ZFS ] F8 switch │ │ │ │ [x] ada0 238 GB Samsung SSD 860 EVO │ │ ├─ "BACKUP" 212 GB ufs │ │ └─ "EFI" 260 MB fat32 (ESP) │ │ │ │ [x] ada1 931 GB WDC WD10EZEX-08WN4A0 │ │ └─ "vault" 931 GB zfs (zpool: tank) │ │ │ │ [x] ada2 238 GB Samsung SSD 860 EVO │ │ │ │ · da0 15 GB SanDisk Ultra USB (install media) │ │ │ │ 3 disks marked · 1,407 GB raw │ │ Space marks · Enter lays out the pool · F8 back to UFS │ │ │ ├────────────────────────────────────────────────────────────────────────┤ │ < Continue > < Back > < Rescan > │ └────────────────────────────────────────────────────────────────────────┘
Two panes: the marked disks on the left and the pool's vdevs on the right. → moves the highlighted disk into the highlighted vdev, and ← takes it back out. The usable size and failure tolerance update as you go.
┌───────────────────── ZFS Pool Layout · zroot ──────────────────────┐ │ │ │ Move disks into vdevs with ← →. T cycles the vdev type. │ │ │ │ MARKED DISKS │ POOL zroot │ │ │ │ │ ada1 931 GB WDC WD10EZEX │ ▾ vdev 1 mirror 238 GB │ │ │ ada0 238 GB Samsung 860 │ │ │ ada2 238 GB Samsung 860 │ │ │ │ │ │ + new vdev (N) │ │ │ │ Usable ≈ 238 GB · survives 1 disk failure in vdev 1 │ │ ! ada1 is marked but not in the pool — move it in, or unmark it. │ │ │ │ ↑↓ move · → into vdev · ← out · Tab pane · T type · N new · D delete │ │ │ ├────────────────────────────────────────────────────────────────────────┤ │ < Continue > < Back > │ └────────────────────────────────────────────────────────────────────────┘
T cycles the highlighted vdev's type. Here four disks were moved into one vdev and cycled to raidz2:
┌───────────────────── ZFS Pool Layout · zroot ──────────────────────┐ │ │ │ MARKED DISKS │ POOL zroot │ │ │ │ │ (all disks placed) │ ▾ vdev 1 raidz2 1.86 TB │ │ │ ada0 931 GB WDC WD10EZEX │ │ │ ada1 931 GB WDC WD10EZEX │ │ │ ada2 931 GB WDC WD10EZEX │ │ │ ada3 931 GB WDC WD10EZEX │ │ │ │ │ │ + new vdev (N) │ │ │ │ Usable ≈ 1.86 TB · survives any 2 disk failures │ │ T: single → mirror → raidz1 → raidz2 → raidz3 │ │ │ ├────────────────────────────────────────────────────────────────────────┤ │ < Continue > < Back > │ └────────────────────────────────────────────────────────────────────────┘
| Key | Action |
|---|---|
| ↑ ↓ | Move the cursor within a pane |
| Tab | Switch between the disk pane and the pool pane |
| → | Move the highlighted disk into the highlighted vdev |
| ← | Move a disk out of its vdev, back to the marked list |
| T | Cycle the vdev type: single → mirror → raidz1 → raidz2 → raidz3 |
| N | Add a new vdev (for striping several mirrors or raidz groups) |
| D | Delete the highlighted vdev; its disks return to the marked list |
| Enter / Esc | Continue when the layout is valid / back to the disk picker |
| Vdev type | Minimum disks | Survives | Source |
|---|---|---|---|
| single | 1 (exactly) | no disk failure | Several singles = a stripe; shown with a warning |
| mirror | 2 | all but one disk | Usable = the smallest disk |
| raidz1 | 2 — 3 recommended | 1 disk | vdev_children < nparity + 1 is rejected in vdev_raidz_open(), and zpool's is_grouping() sets mindev = nparity + 1 |
| raidz2 | 3 — 4+ recommended | 2 disks | |
| raidz3 | 4 — 5+ recommended | 3 disks |
zpool create rejects a mismatched replication level without -f, and the installer should not reach for -f.┌────────────────────────── ZFS Pool Options ──────────────────────────┐ │ │ │ Pool name [ zroot ] │ │ │ │ Sector size ashift 12 (4 KiB) auto from disks │ │ │ │ Compression ( ) off (•) lz4 ( ) zstd │ │ │ │ Encrypt user data [x] zroot/Users — passphrase at boot │ │ Passphrase [ •••••••••••• ] │ │ Confirm [ ••••••••••••_ ] │ │ │ │ The system itself stays unencrypted: the loader reads ZFS but │ │ cannot decrypt it. Swap is encrypted by the pager, and crash │ │ dumps with dumpon -k. │ │ │ │ ↑↓ field · Space toggles · Tab next · Enter continues │ │ │ ├────────────────────────────────────────────────────────────────────────┤ │ < Continue > < Back > │ └────────────────────────────────────────────────────────────────────────┘
com.datto:encryption in features_for_read, so it can open a pool that uses encryption, but stand/libsa/zfs has no decryption code at all. An encrypted root dataset would leave the loader unable to read /boot/kernel/kernel, and the system would not boot.encryption=off child under an encrypted parent (dmu_objset_create_crypt_check, and libzfs's zfs_crypto_create), and swapify needs it unencrypted. Swap is still protected by the pager (Z5), and dumps by dumpon -k.rc.d/dumpon strips the .eli suffix and dumps beneath geli, so a dump written into a pool on geli would land underneath the encrypted data it lives in.Continuing the first layout example after ada1 was unmarked: a two-disk mirror. The review names every volume that will be destroyed, disk by disk.
┌──────────── Review · nothing is written until Install ─────────────┐ │ │ │ POOL zroot · mirror (ada0, ada2) · ≈ 238 GB · 1 failure │ │ EACH DISK gpt: efi 260 MB · freebsd-zfs rest · no swap │ │ │ │ DATASETS zroot/ROOT/default / │ │ zroot/private /private canmount=off │ │ zroot/private/var /private/var canmount=off │ │ zroot/private/var/vm swap + corefile │ │ zroot/private/var/crash savecore output │ │ zroot/Users /Users encrypted │ │ │ │ ERASES ada0 "BACKUP" "EFI" ada2 (no volumes) │ │ │ ├────────────────────────────────────────────────────────────────────────┤ │ < Install > < Back > │ └────────────────────────────────────────────────────────────────────────┘
Roughly what the engine runs for the mirror above. It is illustrative, not the engine code:
# each disk: EFI system partition + ZFS, no swap gpart create -s gpt ada0 gpart add -t efi -s 260m -l efi0 ada0 gpart add -t freebsd-zfs -l zfs0 ada0 # same for ada2 -> efi2 / zfs2 zpool create -o ashift=12 -O compression=lz4 -O atime=off \ -O canmount=off -O mountpoint=none -R /mnt \ zroot mirror gpt/zfs0 gpt/zfs2 zfs create -o canmount=off -o mountpoint=none zroot/ROOT zfs create -o mountpoint=/ zroot/ROOT/default zfs create -o canmount=off -o mountpoint=/private zroot/private zfs create -o canmount=off zroot/private/var zfs create -o checksum=off -o compression=off -o dedup=off -o copies=1 \ -o recordsize=128K -o encryption=off \ -o com.sun:auto-snapshot=false zroot/private/var/vm zfs create -o exec=off -o setuid=off zroot/private/var/crash zfs create -o encryption=on -o keyformat=passphrase \ -o keylocation=prompt -o mountpoint=/Users zroot/Users zpool set bootfs=zroot/ROOT/default zroot
| Needs | Why |
|---|---|
| Z0 · kernel#230 | ZFS compiled into the kernel, plus zfs/zpool in the image. Today they exist only in /rescue. |
| A6 · userland#200 | Without /etc/fstab or rc.d, nothing mounts the datasets beyond zroot/ROOT/default, and nothing prompts for the zroot/Users passphrase. |
| U2 · userland#179 | This design supplies the dataset layout that U2 creates. |
| Z3 · Z4 | Swapify needs zroot/private/var/vm with exactly these properties. Z4 skips it during recursive snapshots. |
zroot/Users) as drawn, because the loader cannot decrypt. Is /Users the home location? do-install.sh:264 preserves both /home and /Users, so it is not settled.