Month: November 2026

Boot Environments Saved My Upgrade Weekend (And Yours Should Too)

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.

Boot Environments Saved My Upgrade Weekend (And Yours Should Too)

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.

Boot Environments Saved My Upgrade Weekend (And Yours Should Too)

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.

Boot Environments Saved My Upgrade Day (And They Can Save Yours)

Why I stopped fearing freebsd-update

For years my upgrade routine was the same: back up what I could remember to back up, run freebsd-update, hold my breath, and hope. Sometimes it worked fine. Sometimes a kernel module didn’t rebuild right, or a port that depended on some ABI quirk fell over, and I’d spend the evening trying to remember what state things were in before I touched anything. That’s not an upgrade workflow, that’s gambling with extra steps.

Boot environments fixed this for me. If you’re running ZFS as your root filesystem on FreeBSD, which you really should be at this point, you already have the machinery to make every upgrade trivially reversible. You just need to actually use it instead of treating ZFS like “the thing that makes snapshots sound fancy.”

What a boot environment actually is

A boot environment is just a clone of your root dataset that the bootloader knows how to boot into. Instead of upgrading your one and only root filesystem in place, you clone it, upgrade the clone, and if the clone turns out to be garbage you reboot into the old one like nothing happened. No restore process, no re-imaging, no digging through a tarball at 2am. You just pick the old entry at the boot menu.

The tool that makes this painless is bectl, which ships in base on current FreeBSD releases. If you’re still on something old enough that it’s not there, go grab it from ports or just upgrade your system properly first, which, yes, is a bit of a chicken-and-egg problem the first time.

Setting it up

Assuming you installed FreeBSD with ZFS on root (the installer defaults to this now, and if you didn’t pick it, that’s a different and more painful conversation), you likely already have boot environments working whether you knew it or not. Check with:

  • bectl list — shows your existing boot environments, which dataset is active, and which one is set to boot next.
  • bectl create name — makes a new boot environment as a clone of the currently active one.
  • bectl activate name — marks a boot environment as the one to boot into on next restart.
  • bectl destroy name — removes one you no longer need, once you’re confident you won’t want to roll back.

That’s genuinely most of the command surface you need for day-to-day use. It’s refreshingly small.

Boot Environments Saved My Upgrade Day (And They Can Save Yours)

My actual upgrade routine now

Here’s what I do before any major version jump or even a risky patch update:

  • Create a fresh boot environment and give it a name that means something to future-me, not “be1” — I use something like pre-14.1-upgrade.
  • Activate it, then reboot into it, just to confirm the mechanism works before I start messing with the system. Trust but verify.
  • Run the actual upgrade — freebsd-update, a full source build, whatever the situation calls for — inside that boot environment.
  • Reboot into the new environment and poke at it. Check services, check dmesg for anything ugly, check that the network actually comes up the way it’s supposed to.
  • If it’s broken, reboot and pick the old boot environment from the loader menu. If it’s fine, I leave both around for a week or two before cleaning up, because some problems don’t show up for a few days.

This is slower than just running the upgrade blind, by maybe five minutes of extra typing. Those five minutes have saved me entire evenings more than once.

The afternoon it actually mattered

I’ll be specific about the one time this really paid off, without turning it into a war story longer than the how-to itself. I was upgrading a box that also ran a jail hosting some internal tooling I actually used daily. The upgrade went through clean, no errors, nothing in the logs that looked alarming. Rebooted, everything came up.

Except the jail’s network stack was behaving strangely — intermittent packet loss that only showed up under load, which of course meant it didn’t show up during my five-minute post-upgrade poke-around. It only became obvious a few hours later when things I actually depended on started timing out. Normally this is the point where you start down a debugging rabbit hole with no clear end, probably involving pcap captures and increasingly specific Google searches.

Instead I just activated the previous boot environment, rebooted, and was back to a known-good state in under two minutes. Then I debugged the problem at my leisure, in a non-production boot environment, with zero time pressure. Turned out to be an interaction with a sysctl tuning value I’d forgotten I’d set, unrelated to the upgrade itself, but that’s beside the point — the point is I had the luxury of figuring that out calmly instead of panicking at 11pm.

Boot Environments Saved My Upgrade Day (And They Can Save Yours)

A few things worth knowing

  • Boot environments aren’t a substitute for real backups. They protect you against bad upgrades and config mistakes on the same disk, not against disk failure, which will absolutely still ruin your week.
  • Don’t let boot environments pile up forever. Each one holds onto the blocks it diverged from, so old ones you’ll never roll back to are just eating disk space for no benefit.
  • If you dual-boot or have a complicated loader setup, boot environments play nicely with the default FreeBSD boot menu, but double-check your loader config if you’ve customized it heavily.

Just use them

If you’re running FreeBSD with ZFS root and you’re not using boot environments for upgrades, you’re doing the dangerous version of something that has a safe version built right into the base system for free. There’s no real downside here beyond a few extra commands and a little bit of disk space. Start using bectl before your next upgrade, not after the one that goes badly.

Powered by WordPress & Theme by Anders Norén