Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

0005 — Dataset layout & boot-environment scope

Status: accepted (2026-06-22) — ratified by maintainer; proposed 2026-06-04

Context

/boot lives inside each BE on the encrypted pool (ADR 0003 / SPEC §3), so a BE rolls back / clones as a unit. That raises the layout question: which datasets travel with the BE (clone + roll back with it) and which are persistent (survive a BE switch/rollback)? SPEC §3 locks “discover any mountable dataset; no enforced layout” for boot-time discovery — this ADR sets the installer default + the consistency rules, not a hard requirement keyboot enforces at boot.

Two correctness constraints drive it:

  • /usr ↔ package DB lockstep. /var/lib/{dpkg,rpm,portage} records what is installed; if it can roll back independently of /usr, a rolled-back system has a package DB that disagrees with its binaries. So the package DB must move with the BE.
  • zfs rollback is per-dataset (ADR 0007): there is no atomic roll-back-these-five-datasets, so the fewer datasets that must move together, the simpler and safer rollback is.

Decision

BE root dataset = rpool/ROOT/<be>, and it INCLUDES /var (so the package DB rolls back in lockstep with /usr). Only binary-independent leaves are split out.

Two dataset classes, distinguished purely by where the dataset lives:

  • BE-scoped — children of rpool/ROOT/<be> (e.g. rpool/ROOT/<be>/home): cloned and rolled back with the BE. This is the default for /home.
  • Persistent — outside rpool/ROOT (e.g. rpool/data/home, rpool/var/log, rpool/data/...): survive BE switch/rollback. For state that must not revert, and for datasets that want their own recordsize/props.

Installer default profile (extends the SPEC §13 interactive sketch; today install-os creates only rpool/ROOT/<distro>):

  • rpool/ROOT/<be> — root incl. /usr, /etc, /var (+ package DB)
  • rpool/ROOT/<be>/home/home, BE-scoped by default
  • rpool/var/log — persistent, sync=always, exec=off
  • (opt) rpool/data/homepersistent /home (the opt-out)
  • (opt) rpool/var/lib/docker, rpool/data/... — persistent, tuned per use

recordsize and other props are set per dataset where it pays (e.g. a DB dataset at 16K vs the 128K default) — the reason to split a leaf at all.

The /var line: stays in the BE — package DB, and anything whose state must match the binaries. Split out only genuinely-independent leaves: /var/log, /var/cache, /var/lib/<db>, /var/lib/docker.

Refinement (2026-06-05, GL#17): parameterize; defer the default profile

The profile above is the recommended reference, not what the installer bakes in. Per operator decision, the layout is parameterized and the installer default stays minimal (rpool/ROOT/<distro> only, as today). Operators / Ansible spell out the leaves they want:

  • install-os --dataset '<ds>[:<mnt>][:<props>]' (repeatable). The {BE} token expands to the BE dataset, so --dataset '{BE}/home:/home' is BE-scoped and --dataset 'rpool/var/log:/var/log:exec=off' is persistent. Props (incl. recordsize, compression, user props like com.sun:auto-snapshot=false) are passed through. Datasets are created after the BE is mounted, canmount=on so they auto-mount on first boot.
  • Swap is a separate param (--swap, follow-up: per-distro fstab + the zvol-deadlock caveat).

This avoids the installer making fleet policy decisions; an opinionated default profile (turning the table above into a one-flag preset) is deferred to a later pass. So /home placement is no longer a baked default — it’s whichever --dataset form the operator passes (the two-homes rule, expressed explicitly).

Alternatives considered

  • /var fully separate / persistent: breaks the /usr↔package-DB lockstep. Rejected — split only binary-independent leaves.
  • /home always persistent (outside the BE) by default: safest against data loss, but a BE rollback then can’t give a consistent point-in-time (home stays current). Made the opt-out (rpool/data/home), not the default. The BE-scoped default is safe because the default rollback verb is non-destructive clone-and-boot (ADR 0007) — current home is never destroyed.
  • Flat single dataset (no splits): no sub-system rollback, no per-dataset tuning. Rejected.

Consequences

  • A BE-scoped /home forks per BE (an upgrade clone gets a CoW copy; work in the new BE isn’t in the old). Acceptable given clone-and-boot rollback + frequent snapshots (ADR 0006/0007); workstation operators who dislike it flip to rpool/data/home.
  • install-os must grow a dataset profile (it currently creates only rpool/ROOT/<distro>). The profile is operator-overridable.
  • The “what stays in /var vs splits out” rule is documented so rollbacks stay internally consistent.
  • Boot-time discovery stays layout-agnostic (SPEC §3); this ADR governs the installer + the consistency rules only.