The upgrade that didn’t need to be scary
I used to treat freebsd-update runs like a minor religious event. Back up the important stuff, cross your fingers, run it, hope nothing in rc.conf or a third-party kernel module decided to have an opinion about the new world order. Then I actually started using ZFS boot environments properly instead of just knowing they existed, and the whole ritual got boring. Boring is good. Boring means I can upgrade a box on a Tuesday afternoon without blocking out my evening in case it goes sideways.
If you’re running FreeBSD on ZFS root, which at this point you probably should be unless you have a specific reason not to, boot environments are sitting right there waiting for you to use them. Most people don’t touch them until something breaks and they’re frantically googling how to roll back. Let’s not do that. Let’s set this up before you need it.
What a boot environment actually is
A boot environment is a bootable ZFS clone of your root filesystem. It’s not a backup in the “copy somewhere safe” sense — it lives on the same pool, shares blocks with the original dataset until things diverge, and the bootloader knows how to pick which one to boot into. The magic is that creating one is nearly instant and nearly free in disk space, because ZFS only writes new blocks as things change.
This means you can snapshot your current working system, make a clone of it, boot into the clone, run your upgrade or your risky experiment there, and if it goes wrong you just reboot back into the old environment like nothing happened. No restore process. No “where did I put that backup.” It’s already sitting on disk, untouched.
Setting it up
The tool you want is bectl, which ships in base on modern FreeBSD. If you’re on an older release you might still be dealing with the third-party beadm port, but bectl is the one to learn going forward.
- Check what you’ve already got with bectl list. On a fresh install you’ll usually see a single default environment, often named something like “default” or tied to the release version.
- Before any upgrade, create a new one: bectl create preupgrade. Name it something that means something to you in six months, not “be1.”
- Activate it for the next boot if you want to actually boot into the clone and test there: bectl activate preupgrade.
- Reboot, confirm you’re in the new environment with bectl list again, the active one gets marked.
Now do your upgrade work inside this environment. If it’s a freebsd-update run, a pkg upgrade of something gnarly, or you’re testing a new kernel config, you’re doing it on a disposable clone of your system. Worst case, you boot back into the environment that worked.

The part everyone skips: mountpoints and data
Boot environments only cover your root filesystem and whatever datasets are mounted under it that aren’t explicitly excluded. Your /usr/home or /var/db data living on separate datasets generally stays shared across environments, which is usually what you want — you don’t want your mail spool or your jail data reverting when you roll back the OS. But it does mean a boot environment isn’t a full system backup. If an upgrade corrupts data in a shared dataset, rolling back the boot environment won’t save you. I learned this distinction the hard way when I assumed rolling back would undo a package’s writes to a shared config directory. It did not. The OS rolled back fine; the package’s leftover junk in /usr/local/etc stayed right where it broke things.
Lesson: for anything genuinely precious, still take a real snapshot of the relevant dataset separately, or at minimum know which datasets are shared before you assume a rollback fixes everything.
Rolling back when things go wrong
This is the actual payoff. Say the upgrade finished, you rebooted, and now something’s broken — a driver won’t load, a service refuses to start, whatever. You don’t troubleshoot under pressure. You just boot back into the previous environment:
- At the boot loader menu, FreeBSD will list available boot environments if you’re using the default loader setup. Pick the old one and boot it directly without needing to run bectl at all.
- Or, once you’re in any working environment, run bectl activate oldname to make it the default for next boot, then reboot normally.
Either way you’re back on the known-good system in the time it takes to reboot, not the time it takes to restore from backup and cross your fingers that the backup was current.

Cleaning up after yourself
Boot environments are cheap but not free. Old ones hang around holding onto blocks that would otherwise be freed, especially once things diverge significantly after a few upgrade cycles. Once you’re confident an upgrade was successful and you don’t need the fallback anymore, destroy it: bectl destroy preupgrade. I keep exactly one or two old environments around at a time, named for what they were for, and nuke the rest. There’s no prize for hoarding boot environments named be1 through be47.
A habit worth building
The whole point of this isn’t some elaborate system — it’s a two-command habit. Before anything risky: create and activate a new boot environment. After it’s confirmed working: clean up the old one. That’s the entire workflow, and it turns “upgrade day anxiety” into “upgrade whenever, who cares.” If you’re running FreeBSD on ZFS and not using boot environments yet, this is the thing to fix before your next freebsd-update run, not after the one that goes badly.

