| pick your distro, get ZFS on root
kldload — your platform, your way, free
Source

Boot Environments — upgrade without fear

A boot environment (BE) is a complete, bootable snapshot of your root filesystem. On kldload, each BE is a ZFS dataset under rpool/ROOT/. You can have ten different states of your system sitting on one disk — each independently bootable, consuming only the space of the changes between them.

The promise: Before you upgrade the kernel, run a dist-upgrade, or install a new desktop environment, create a boot environment. If anything breaks, reboot and pick the previous BE from ZFSBootMenu. Two commands, thirty seconds, and you are back to exactly where you were — packages, configs, everything.

This is the FreeBSD beadm model, brought to Linux. FreeBSD has had this for over a decade. kldload makes it work on CentOS and Debian.


What is a boot environment, exactly?

kldload installs your root filesystem as a ZFS dataset: rpool/ROOT/default. When you create a boot environment named pre-upgrade, ZFS clones that dataset to rpool/ROOT/pre-upgrade. The clone is instantaneous and starts at zero extra space. It only grows as the two environments diverge.

ZFSBootMenu reads the list of bootable datasets from the pool at startup and shows you a menu. You pick which one to boot. No grub entries to edit, no dracut regeneration — ZFSBootMenu does all of that automatically.

# What the dataset tree looks like after a few BEs
rpool/ROOT/default          <-- current running system
rpool/ROOT/pre-upgrade      <-- checkpoint before last upgrade
rpool/ROOT/stable-march     <-- known-good state from March

kbe command reference

List all boot environments

kbe list

Expected output:

NAME              ACTIVE  MOUNTPOINT  SPACE    CREATED
default           yes     /           1.2 GiB  2026-01-15
pre-upgrade       no      -           84 MiB   2026-03-20
stable-march      no      -           310 MiB  2026-03-01

Space shown is the unique data in each BE — shared blocks are counted only once. The ACTIVE column shows which dataset is mounted as / right now.

Create a boot environment

kbe create my-checkpoint
kbe create pre-upgrade-$(date +%Y%m%d)

Creation is instantaneous. It is a ZFS clone — no data is copied.

Activate a boot environment

kbe activate pre-upgrade

Sets the bootfs property on the pool so ZFSBootMenu boots that dataset on next boot. The currently running system is not affected — you have to reboot for the change to take effect.

[kbe] setting rpool/ROOT/pre-upgrade as default boot dataset
[kbe] will take effect on next boot
[kbe] run: reboot

Roll back a boot environment

kbe rollback pre-upgrade

Activates the named BE and immediately reboots. This is the emergency recovery path.

[kbe] WARNING: this will reboot immediately
[kbe] activating rpool/ROOT/pre-upgrade...
[kbe] rebooting now

Delete a boot environment

kbe destroy old-environment

Destroys the ZFS dataset. You cannot destroy the currently active BE.


Safe upgrade workflow

This is the recommended pattern for any significant system change:

# 1. Create a checkpoint BE before you start
kbe create pre-upgrade-$(date +%Y%m%d)

# 2. Verify it is there
kbe list

# 3. Do your upgrade (or use kupgrade for automatic snapshot + DKMS check)
dnf upgrade -y
# -- or --
apt dist-upgrade -y

# 4. Reboot into the new system
reboot

# 5. If everything works, optionally clean up old BEs
kbe list
kbe destroy pre-upgrade-20260101   # remove stale checkpoints

If the upgrade breaks something:

# Option A: activate at the command line and reboot manually
kbe activate pre-upgrade-20260325
reboot

# Option B: at ZFSBootMenu, press [E] to open the BE selector
# Arrow to your old BE, press Enter to boot it

ZFSBootMenu integration

ZFSBootMenu is the bootloader kldload installs by default. It replaces GRUB for ZFS-on-root systems and understands native ZFS datasets.

At boot time, ZFSBootMenu:

  1. Scans the pool for all datasets with mountpoint=/
  2. Shows a menu if more than one bootable dataset exists, or if you press a key during the timeout
  3. Boots the dataset marked as bootfs automatically after the timeout
  4. Handles kernel selection within a dataset (multiple kernels per BE)

Key bindings inside ZFSBootMenu:

Key Action
Enter Boot selected environment
E Edit kernel command line
S Open snapshot browser for the selected BE
D Set selected BE as default
R Recovery shell (drops to a rescue busybox shell)

The snapshot browser (S) is particularly useful: it lists raw ZFS snapshots of a BE and lets you boot directly into a snapshot as a read-only environment to inspect the system state at that point in time.


Compared to FreeBSD beadm

What is the same

The model is identical: BEs are ZFS clones of ROOT, stored under a common parent dataset, each independently bootable. kbe list/create/activate/destroy maps directly to beadm list/create/activate/destroy.

What is different

FreeBSD uses the BIOS/UEFI bootcode directly; kldload uses ZFSBootMenu as a thin EFI binary. kldload also manages DKMS module signing per-BE, which FreeBSD does not need (no out-of-tree kernel modules).

The gap kldload fills

On a stock Linux system with ZFS you can take a snapshot, but making it bootable requires manually editing GRUB entries. kldload + kbe + ZFSBootMenu makes the entire cycle automatic and scriptable.


Tips and gotchas

  • Always create a BE before dist-upgrades, kernel changes, or large package operations. It costs you nothing and saves your system if something goes wrong.
  • /home, /var, and other non-root datasets are not rolled back when you activate an old BE. Only rpool/ROOT/<name> is switched. This is usually what you want — your data stays current, only the OS reverts.
  • If you roll back a BE while your application database is on rpool/data, the old OS code may not be compatible with the current database schema. Consider snapshotting data datasets separately before upgrades.
  • Boot environments accumulate. Run kbe list occasionally and destroy ones you no longer need.
  • kupgrade creates a BE automatically before every upgrade. See the kupgrade guide for the full workflow.