ZFS Pool Setup_

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.

PRELIMINARY · WILL BE REFINED TICKET U4 · userland#201 EPIC E14 · nextbsd#468 NEEDS Z0 · kernel#230 NEEDS A6 · userland#200 NO SWAP PARTITION
Preliminary design — not final. This draft is a starting point for the ZFS install path in U4 (nextbsd-userland#201), and it will be refined before implementation begins. Treat the screens, keys and defaults as proposals, not a spec.

1Where ZFS fits

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.

Install Mode→ Disk Picker→ F8 · ZFS→ Pool Layout→ Pool Options→ Review→ Account & Hostname→ Install

2Disk Picker · F8 switches 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.

PHASE 2 · UFS MODE (DEFAULT)
┌────────────────────────  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.

PHASE 2 · ZFS MODE (AFTER F8)
┌─────────────────────────  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 >               │
└────────────────────────────────────────────────────────────────────────┘

3Pool Layout · arrow disks into vdevs

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.

PHASE 3 · LAYOUT IN PROGRESS
┌─────────────────────  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:

PHASE 3 · RAIDZ2, READY
┌─────────────────────  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 >                      │
└────────────────────────────────────────────────────────────────────────┘
KeyAction
↑ ↓Move the cursor within a pane
TabSwitch 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
TCycle the vdev type: single → mirror → raidz1 → raidz2 → raidz3
NAdd a new vdev (for striping several mirrors or raidz groups)
DDelete the highlighted vdev; its disks return to the marked list
Enter / EscContinue when the layout is valid / back to the disk picker
Vdev typeMinimum disksSurvivesSource
single1 (exactly)no disk failureSeveral singles = a stripe; shown with a warning
mirror2all but one diskUsable = the smallest disk
raidz12 — 3 recommended1 diskvdev_children < nparity + 1 is rejected in vdev_raidz_open(), and zpool's is_grouping() sets mindev = nparity + 1
raidz23 — 4+ recommended2 disks
raidz34 — 5+ recommended3 disks

4Pool Options

PHASE 4 · POOL OPTIONS
┌──────────────────────────  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 >                      │
└────────────────────────────────────────────────────────────────────────┘

5Review · nothing is written until Install

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.

PHASE 5 · REVIEW
┌────────────  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:

ENGINE · ILLUSTRATIVE
# 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

6What this depends on

NeedsWhy
Z0 · kernel#230ZFS compiled into the kernel, plus zfs/zpool in the image. Today they exist only in /rescue.
A6 · userland#200Without /etc/fstab or rc.d, nothing mounts the datasets beyond zroot/ROOT/default, and nothing prompts for the zroot/Users passphrase.
U2 · userland#179This design supplies the dataset layout that U2 creates.
Z3 · Z4Swapify needs zroot/private/var/vm with exactly these properties. Z4 skips it during recursive snapshots.

7Open decisions — to settle while refining