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

Dataset layout — a reference for --dataset

keyboot does not bake an opinionated dataset profile. By default install-os creates only the BE root (rpool/ROOT/<be>); you spell out the leaves you want with --dataset (repeatable), and an opinionated default profile is a future addition (parameterize-now, defaults-later — ADR 0005 refinement). This page is the reference for what to pass.

The rule (ADR 0005)

  • Binary-coupled state stays in the BE root — anything whose state must match the installed binaries rolls back as one atomic unit. Notably /var (including the package DB) and /etc stay in the BE.
  • Split out only binary-independent leaves — data whose lifetime is independent of the OS image, or that wants its own recordsize/snapshot policy. That’s the only reason to make a separate dataset.

Two classes, distinguished purely by where the dataset lives:

  • BE-scoped — a child of rpool/ROOT/<be> (e.g. {BE}/home): clones + rolls back with the BE.
  • Persistent — outside rpool/ROOT (e.g. rpool/var/log): survives a BE switch/rollback.

--dataset encodes this with the {BE} token: {BE}/... is BE-scoped, anything else is persistent.

Reference table

PathPlacement--dataset exampleProps / notes
/ (/usr /etc /bin)BE root(created by default)rolls back as a unit
/var (+ /var/lib/<pkgdb>)in BE root(stays in BE)pkgdb lockstep with /usr
/homeBE-scoped or persistent'{BE}/home:/home' or 'rpool/data/home:/home'two-homes rule — pick per host
/var/logpersistent'rpool/var/log:/var/log:exec=off'logs survive a rollback (so you can read why you rolled back)
/var/cachepersistent, discardable'rpool/var/cache:/var/cache:com.sun:auto-snapshot=false'don’t snapshot heavy caches
/var/lib/<db>persistent'rpool/data/<db>:/var/lib/<db>:recordsize=16K'DB page recordsize (8K/16K); own snap policy
/var/lib/dockerpersistent'rpool/data/docker:/var/lib/docker'container store
/srvpersistent'rpool/srv:/srv'app data
/tmptmpfs (not ZFS)never snapshot
/boot/efi (ESP)vfat mdraid1 (not ZFS)keyboot-owned; ro-by-default (ADR 0004)
swappartition or zvol--swap (separate param)not on a snapshotted dataset

Intermediate parents (e.g. rpool/var for rpool/var/log) are auto-created with mountpoint=none, so they never shadow /.

Set where it pays — the reason to split a leaf at all:

  • compression=lz4 (cheap, near-universal win; zstd for cold/archival data)
  • atime=off (avoid write amplification on reads)
  • xattr=sa, acltype=posixacl (SA xattrs; needed by systemd/journald, samba)
  • recordsize: leave the 128K default for general data; 8K–16K for DB datasets matching their page size
  • exec=off / setuid=off / devices=off on data-only datasets (/var/log, /srv, caches) — defence in depth
  • com.sun:auto-snapshot=false on discardable datasets (/var/cache, swap)

Swap (--swap, GL#35)

install-os --swap none|zvol:<size> (default none). zvol:<size> creates <pool>/swap as a ZFS volume and mkswaps it. Because the zvol lives on the pool, swap is encrypted at rest via the same LUKS keyfile as everything else — no extra key management.

The zvol uses OpenZFS swap-tuned props (sync=always, primarycache=metadata, secondarycache=none, logbias=throughput, compression=zle, 4K volblock, com.sun:auto-snapshot=false).

Boot-time activation is a dedicated keyboot-swap service, not fstab. At boot the zvol’s device nodes fire their uevents inside keyboot’s initramfs (before the BE’s real udev), and minimal BEs lack the ZFS udev rules that make /dev/zvol/<pool>/swap, so a plain swapon -a from fstab races a non-existent symlink. Instead /usr/local/sbin/keyboot-swapon re-triggers udev and swapons the swap-signed zdN device directly (validated by swapon itself, so no blkid flag dependence). It runs via an openrc init service in the boot runlevel or a systemd oneshot (keyboot-swap.service, WantedBy=multi-user). The install marker prints KEYBOOT-SWAP <active-count> so the boot test can assert swap actually came up.

Caveat — swap-on-zvol can deadlock under heavy memory pressure (ZFS may need to allocate memory to write out swap — the memory it’s trying to free). It’s fine for moderate overcommit, but for swap-heavy workloads a raw, keyfile-encrypted swap partition is safer. That backing (--swap partition:<size>) is deferred to GL#34: it can’t live on the pool, so it needs the partition-role work.

Example: a typical server

install-os debian --disk … \
  --dataset '{BE}/home:/home' \
  --dataset 'rpool/var/log:/var/log:exec=off' \
  --dataset 'rpool/var/cache:/var/cache:com.sun:auto-snapshot=false' \
  --dataset 'rpool/data/docker:/var/lib/docker'

This table is a recommendation, not a baked default. Fleet policy belongs in Ansible (the installer accepts a spec; ADR 0005 / 0015). An opinionated one-flag default profile is deferred to a future pass.