Solved Activate BE before reboot

I still do not quite understand how, and in what order, ZFS boot environment (BE) interacts with the running kernel, with a new kernel, and with userland. I have some doubts about the order of the steps in FreeBSD 15.1-RELEASE Upgrading Instruction via freebsd-update(1). Is it meant only for UFS? In the case of ZFS, the sequence of operations seems quite different to me:

1. Apply all pending patches:
freebsd-update fetch install
2. Upgrade the kernel:
freebsd-update upgrade -r 15.1-RELEASE
freebsd-update install
6…9. Upgrade the boot loader
loaders-update show-me
loaders-update shoot-me
+ Replace an active BE with the new one
bectl create pre-15.1
bectl activate pre-15.1
3. Reboot into the new kernel
4. Upgrade the userland:
freebsd-update install
5. Remove stale files:
freebsd-update install
10. Reboot:
shutdown -r now
+ Check the system. If all services are working properly, save and activate the new boot environment:
bectl create post-15.1
bectl activate post-15.1

I dared to upgrade FreeBSD from 15.0-R to 15.1-R in this order, and all went smoothly. But I am pretty new to the Unix environment, so I would appreciate any corrections and explanations.
 
My opinions/understanding. May not be strictly correctly, but should be close enough.

freebsd-update is filesystem agnostic. It works fine on UFS and ZFS. BUT freebsd-update will not work or may cause problems on a pkgbase system.
The link to Upgrading you have clearly lists steps for "default install" and "PkgBase" system

On ZFS freebsd-update knows about it and when you run the first "freebsd-update install" by default will create a new BE that represents the system BEFORE anything is updated. That means all updates will be applied to the BE you are currently booted.

A BE is at it's core a ZFS clone (writeable snapshot of a dataset), containing "things" starting at the root directory ("/") that are not part of another dataset. So directories (not an exhaustive list) like /boot, /etc, /sbin, /bin, /usr/sbin, /usr/bin, /usr/local are part of a BE. Current running kernel comes from /boot/kernel, configuration comes from /etc, userland is in /sbin, /bin, /usr/sbin, /usr/bin.

You are mixing instructions used for a default install (non pkgbase) and for a system converted to pkgbase.
Default installation on 15.x I think is still non-pkgbase so freebsd-update is the tool to use.

If your system has not been explicitly converted to pkgbase, the bectl create and activate commands you list are not needed, worst case you have extra BEs that you don't need.
bectl list will give the BEs you have.

Default install (non pkgbase) steps are:
  1. freebsd-update fetch
  2. freebsd-update install
  3. freebsd-update upgrade -r 15.1-RELEASE
  4. freebsd-update install this step updates kernel
  5. edit /etc/rc.conf and /boot/loader.conf to not load any modules like i915/nvidia
  6. shutdown -r now
  7. freebsd-update install this step updates userland
  8. freebsd-update install this step may cleanup old libs
  9. pkg upgrade -f this upgrades all applications/ports
  10. reenable any changes in /etc/rc.conf
  11. freebsd-version -kru to show currently running kernel, kernel version after a reboot and userland versions.
step 5 is helpful when you are upgrading across releases, can prevent issues on reboot because of KABI mismatch.
step 8 may say nothing to do
you can reboot after step 10 or manually load any modules you commented out.
 
Thanks mer, your 5th and 10th steps to temporally disable some new firmware kmods are important.

"freebsd-update install" by default will create a new BE that represents the system BEFORE anything is updated. That means all updates will be applied to the BE you are currently booted.
Yes, and that is my main concern. IIRC, reboot will return to an old, outdated system, which is marked R (on Reboot) as an active BE in ZFS. Unfortunately, I did not find clear explanation in the docs about how this interacts with the upgrade process.
 
Sorry, this is going to get long.
step 5 yes it temp disables anything that may cause a problem on the initial reboot into the new kernel. You should do this going across releases (say 14 to 15, 15.0 to 15.1). It is only temporary. Step 9 updates all the ports to match the new version, step 10 you reverse what you did in step 5 because now everything in the BE matches.

I'm not sure where you came up with your list of steps but it seems to be mix of default install (distribution sets) and pkgbase or just confusion/mixed sources.

Key points about freebsd-update:
  • freebsd-update, by default ALWAYS works on the BE you are currently in. (yes there are ways to chroot and stuff but I'm saying by default).
  • freebsd-update, by default ALWAYS creates a new BE BEFORE the install command applies any changes.
  • freebsd-update fetch/upgrade only retrieves patchs but does NOT apply them.

The bectl steps you have in your post don't do anything useful, just add confusion. Lets walk through your steps; I'm assuming that you are running a default install, NOT a PKGbase system.

Starting point: system booted running 15.0-RELEASE-p10, the current BE is named "default" (bectl list with NR flags, only has one BE named default)
  1. freebsd-update fetch install update the BE named default to latest 15.0-RELEASE may not do anything
  2. freebsd-update upgrade -r 15.1-RELEASE "fetch the patches to go to 15.1-RELEASE-p0"
  3. freebsd-update install updates the kernel in the BE named default to 15.1-RELEASE-p0 and creates a timestamped new BE that is the 15.0-RELEASE-p10.
  4. update bootloaders
  5. bectl create pre-15.1 create new BE which is "default" plus 15.1 kernel with 15.0 userland
  6. bectl activate pre-15.1 set to be used on reboot
  7. reboot You are now in a BE named "pre-15.1" running the 15.1 kernel with 15.0 userland
  8. freebsd-update install installs 15.1 userland in the current BE (pre-15.1)
  9. freebsd-update install clean up stale files
  10. reboot You are now in a BE named "pre-15.1" running 15.1kernel with 15.1 userland (freebsd-version -kru)
  11. bectl create post-15.1 creates new BE which is your fully upgraded pre-15.1
  12. bectl activate post-15.1 set for next boot

At this point if you do bectl list you have something like maybe 4 BEs total:
default
a 15.0 timestamped created by the first freebsd-update install of the 15.1 kernel
pre-15.1 which was updated completely to 15.1
post-15.1 which is basically just the fully upgraded pre-15.1

A lot of extra work for not much gain (my opinion).

Thanks mer, your 5th and 10th steps to temporally disable some new firmware kmods are important.


Yes, and that is my main concern. IIRC, reboot will return to an old, outdated system, which is marked R (on Reboot) as an active BE in ZFS. Unfortunately, I did not find clear explanation in the docs about how this interacts with the upgrade process.
bectl activate marks the BE as to be active on the next reboot; the contents may or may not be an outdated/older system. Just because you created and named a BE pre-15.1 does not mean the contents are 15.0. That's why I walked through your steps above. Think about "what BE am I currently in when I do this freebsd-update command", then what the bectl create command actually does.

Since by default freebsd-update works on the current BE and will create a timestamped BE that is before anything is applied, one does not need to manually create a BE.

Sit down with a piece of paper or whiteboard, walk through the steps 1-12 that I outline here. Draw a box with your starting point, remember the keypoints on freebsd-update, pay attention to "what BE am I in" after a reboot, what should bectl list show. It may take a couple of iterations, but it should eventually make sense.
Hopefully this helps.
 
default is an arbitrary word and can sometimes be misleading. The true key tags are N (running Now) and R (activated on Reboot). The extra work and space are quite minimal, but become crucial for recovery. I usually remove temporary BEs I no longer need (this time, 15.0R10_2026-06-11, pre-15.1 and post-15.1) after a successful upgrade:
Code:
BE                                 Active Mountpoint Space Created
15.0-RELEASE-p10_2026-06-20_152818 -      -          2.61G 2026-06-20 15:28
15.1R0_2026-06-20                  NR     /          61.3G 2026-06-20 16:38

I am afraid that the official instruction for freebsd-update upgrade might be unsafe in case of ZFS. The updater automatically creates a timestamped BE of a former state before upgrade. And reboot reverts an upgraded system to even older BE (marked as R) without my manual new and activated BE before reboot.
 
I am afraid that the official instruction for freebsd-update upgrade might be unsafe in case of ZFS. The updater automatically creates a timestamped BE of a former state before upgrade. And reboot reverts an upgraded system to even older BE (marked as R) without my manual new and activated BE before reboot.
I completely disagree. I (and lots of others) have been using freebsd-update upgrade on ZFS systems since ZFS came into FreeBSD (roughly 9.0).
The timestamped BE that is former state is not marked with "R" by default.
If rebooting gets you back to an older BE then, frankly, you are doing something wrong.

default is an arbitrary word and can sometimes be misleading. The true key tags are N (running Now) and R (activated on Reboot).
"default" is the name of the BE created by the installer, most people never use bectl rename to change it, use freebsd-update to apply patches which create timestamped BEs, freebsd-update upgrade to cross releases.
As a result the "BE named default" (which is how I phrased things above) can go from 13.x to 14.x to 15.x and the name of the BE stays "default" as shown in bectl list.

I'm sorry if what I've said is of no help to your understanding; please ignore everything I've written and keep doing things your way. I'm done with trying to help.
 
The timestamped BE that is former state is not marked with "R" by default.
And what does your bectl list look like after the upgrade? Do the old BE's remain marked with N and R? Although for some reason, the former boot environment becomes as new and active.
 
Two days ago I borked my system while upgrading from 15.0 to 15.1. I think it was something with the graphics drivers... caused a kernel panic and I couldn't move forward. So I re-installed 15.0. I'll revisit that upgrade later once I understand BE's better.

As part of re-installing, I figured I'd learn about bectl and gpt labels... do it 'right' this time. So after 2 days of fiddling I think I understand both a tiny bit, but still I have questions:

General Questions

* What does `bectl` backup? How can I see what it's backing up?
* How do I backup my BE's? Instructions say to backup /var/backups... is that sufficient?
* How would I restore a BE from backup? Is that a thing one would want/need to do?
* I just re-installed my OS several times in the last 2 days and I don't remember whether I used the traditional method or the newer method...how can I tell?
* Vermadden (link below) has instructions about dealing with boot loaders... Is this b/c of version upgrades, from say 15.0 to 15.1, or is this because of geli encrypted drives and zfs? How does bectl (and BEs) interact with boot loaders?

Primary Question:
* If I have a BE named, say, `default` and it's my current BE, so it's `NR`. If I make changes to my OS, will those be reflected in a reboot? From https://freebsdsoftware.org/blog/freebsd-boot-environments/, it seems like the current BE is mutable and, if active, will be rebooted into with the changes.

However, I ask b/c one thing that bit me recently is this:

I was playing around with gpt disk labeling. I labeled my boot and swap disks, updated `/etc/fstab` and then rebooted. I'm not exactly sure which BE I rebooted into, as I was still learning everything. But I wound up in a BE that did not have my `/etc/fstab` changes, so the boot process stopped when it couldn't find the drives. I fixed that be re-labeling the drives so I could boot... after that, I've adopted a strategy of assuming that I need to create a BE after any change to the base OS and then mark that one active, boot into it, destroy and re-create default, mark default active, and then reboot into default. Is this required? Or are changes to the active BE persisted across reboots? It seems that if the BE is a clone of a snapshot, that means it is mutable...but aren't BE's supposed to be snapshots, which implies they are immutable?

Anyway, what's the easiest path forward w/respect to making changes? My ideal would be to stay in a mainline BE, like `default`, mutate it as needed, but snapshot with a BE before making any significant changes. As long as on reboot I still have my state in `default` and I don't get reverted back to when I created the `default` be, thus losing all changes, and forcing me to switch to the most recently created BE as a savepoint.

Context (my system)

Here's some of my system to give you an idea of what I've been doing:

Here are the labels that I created for my boot drive (efiboot0-2tb-samsung, gptboot0-2tb-samsung (note that the gptboot0 is not referenced in /etc/fstab... so what's it for?)) and my swap (swap-2tb-samsung).
The labels for my geli encrypted drives (used in my zfs mirror) are data1-1tb-ct1000 and data2-1tb-ct1000.

Code:
toddg@system76 ~ $ ls /dev/gpt
data1-1tb-ct1000        data1-1tb-ct1000.eli    data2-1tb-ct1000        data2-1tb-ct1000.eli    efiboot0-2tb-samsung    gptboot0-2tb-samsung    swap-2tb-samsung

Note that my fstab doesn't mention the gptboot0 label....

Code:
toddg@system76 ~ $ cat /etc/fstab
# Device                                Mountpoint      FStype  Options         Dump    Pass#
/dev/gpt/efiboot0-2tb-samsung           /boot/efi       msdosfs rw              2       2
/dev/gpt/swap-2tb-samsung                       none    swap    sw              0       0
# Added by auto-admin from /usr/local/sbin/auto-add-fdesc-mount
fdesc                                   /dev/fd                 fdescfs rw              0       0
# End auto-admin addition
# Added by auto-admin from desktop-installer
proc                                    /proc           procfs  rw              0       0
# End auto-admin addition


gpart showing my drives and partitions:

Code:
toddg@system76 ~ $ gpart show
=>        40  3907029088  nda0  GPT  (1.8T)
          40      532480     1  efi  (260M)
      532520        1024     2  freebsd-boot  (512K)
      533544         984        - free -  (492K)
      534528     8388608     3  freebsd-swap  (4.0G)
     8923136  3898105856     4  freebsd-zfs  (1.8T)
  3907028992         136        - free -  (68K)

=>        40  1953525088  nda1  GPT  (932G)
          40        2008        - free -  (1.0M)
        2048  1953521664     1  freebsd-zfs  (932G)
  1953523712        1416        - free -  (708K)

=>        40  1953525088  nda2  GPT  (932G)
          40        2008        - free -  (1.0M)
        2048  1953521664     1  freebsd-zfs  (932G)
  1953523712        1416        - free -  (708K)

=>      63  61112257  da0  MBR  (29G)
        63      1985       - free -  (993K)
      2048  61110272    1  fat32lba  (29G)

gpart showing my drives and labels:

Code:
toddg@system76 ~ $ gpart show  -l
=>        40  3907029088  nda0  GPT  (1.8T)
          40      532480     1  efiboot0-2tb-samsung  (260M)
      532520        1024     2  gptboot0-2tb-samsung  (512K)
      533544         984        - free -  (492K)
      534528     8388608     3  swap-2tb-samsung  (4.0G)
     8923136  3898105856     4  zfs0  (1.8T)
  3907028992         136        - free -  (68K)

=>        40  1953525088  nda1  GPT  (932G)
          40        2008        - free -  (1.0M)
        2048  1953521664     1  data1-1tb-ct1000  (932G)
  1953523712        1416        - free -  (708K)

=>        40  1953525088  nda2  GPT  (932G)
          40        2008        - free -  (1.0M)
        2048  1953521664     1  data2-1tb-ct1000  (932G)
  1953523712        1416        - free -  (708K)

=>      63  61112257  da0  MBR  (29G)
        63      1985       - free -  (993K)
      2048  61110272    1  (null)  (29G)



I have two zfs pools, one for booting (zroot) and another for data and my user account's home directory (zdata)

Code:
toddg@system76 ~ $ zpool list
NAME    SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
zdata   928G   100G   828G        -         -     0%    10%  1.00x    ONLINE  -
zroot  1.81T  13.2G  1.80T        -         -     0%     0%  1.00x    ONLINE

It looks like each BE creates a dataset... that's cool, but I would have thought it'd also create a snapshot (see next):

Code:
toddg@system76 ~ $ zfs list
NAME                                        USED  AVAIL  REFER  MOUNTPOINT
zdata                                      83.1G   816G    96K  /zdata
zdata/DATA                                 82.7G   816G    96K  /zdata/DATA
zdata/DATA/data                            82.7G   816G  82.7G  /data
zdata/toddg                                 383M   816G   383M  /home/toddg
zroot                                      13.2G  1.74T    96K  /zroot
zroot/ROOT                                 10.4G  1.74T    96K  none
zroot/ROOT/15.0-clean                         8K  1.74T  2.42G  /
zroot/ROOT/15.0-desktop-installer             8K  1.74T  7.07G  /
zroot/ROOT/15.0-encrypted-data             5.28M  1.74T  10.0G  /
zroot/ROOT/15.0-firefox-configured         12.3M  1.74T  9.98G  /
zroot/ROOT/15.0-fstab-labels                392K  1.74T  9.97G  /
zroot/ROOT/15.0-fstab-labels-2              700K  1.74T  10.0G  /
zroot/ROOT/15.0-graphics-driver-installed     8K  1.74T  9.84G  /
zroot/ROOT/15.0-i3-installed                278M  1.74T  10.1G  /
zroot/ROOT/default                         10.1G  1.74T  10.0G  /
zroot/home                                   96K  1.74T    96K  /home
zroot/tmp                                  17.6M  1.74T  17.6M  /tmp
zroot/usr                                  2.00G  1.74T    96K  /usr
zroot/usr/ports                            1.05G  1.74T  1.05G  /usr/ports
zroot/usr/src                               978M  1.74T   978M  /usr/src
zroot/var                                   842M  1.74T    96K  /var
zroot/var/audit                              96K  1.74T    96K  /var/audit
zroot/var/crash                             841M  1.74T   841M  /var/crash
zroot/var/log                               388K  1.74T   388K  /var/log
zroot/var/mail                              172K  1.74T   172K  /var/mail
zroot/var/tmp                                96K  1.74T    96K  /var/tmp

I'm confused as to why there are only zroot/ROOT/default snapshots here...

Code:
toddg@system76 ~ $ zfs list -t snapshot
NAME                                       USED  AVAIL  REFER  MOUNTPOINT
zroot/ROOT/default@2026-06-20-13:16:09-0  2.96M      -  2.42G  -
zroot/ROOT/default@2026-06-20-13:29:05-0  12.2M      -  7.07G  -
zroot/ROOT/default@2026-06-20-14:07:44-0  2.68M      -  9.84G  -
zroot/ROOT/default@2026-06-20-17:26:09-0   776K      -  9.84G  -
zroot/ROOT/default@2026-06-21-07:41:50-0  1.02M      -  9.97G  -
zroot/ROOT/default@2026-06-21-08:01:21-0  1012K      -  9.97G  -
zroot/ROOT/default@2026-06-21-08:51:09-0  15.0M      -  10.0G  -
zroot/ROOT/default@2026-06-21-09:08:39-0  1.05M      -  10.0G  -

Here are my BEs:

Code:
toddg@system76 ~ $ bectl list -aDHs
15.0-clean
  zroot/ROOT/15.0-clean -       -       8K      2026-06-20 13:16
    zroot/ROOT/default@2026-06-20-13:16:09-0    -       -       2.96M   2026-06-20 13:16

15.0-desktop-installer
  zroot/ROOT/15.0-desktop-installer     -       -       8K      2026-06-20 13:29
    zroot/ROOT/default@2026-06-20-13:29:05-0    -       -       12.2M   2026-06-20 13:29

15.0-encrypted-data
  zroot/ROOT/15.0-encrypted-data        -       -       5.28M   2026-06-21 08:51
    zroot/ROOT/default@2026-06-21-09:08:39-0    -       -       1.05M   2026-06-21 09:08

15.0-firefox-configured
  zroot/ROOT/15.0-firefox-configured    -       -       12.3M   2026-06-20 17:26
    zroot/ROOT/default@2026-06-21-07:41:50-0    -       -       1.02M   2026-06-21 07:41

15.0-fstab-labels
  zroot/ROOT/15.0-fstab-labels  -       -       392K    2026-06-21 07:41
    zroot/ROOT/default@2026-06-21-08:01:21-0    -       -       1012K   2026-06-21 08:01

15.0-fstab-labels-2
  zroot/ROOT/15.0-fstab-labels-2        -       -       700K    2026-06-21 08:01
    zroot/ROOT/default@2026-06-21-08:51:09-0    -       -       15.0M   2026-06-21 08:51

15.0-graphics-driver-installed
  zroot/ROOT/15.0-graphics-driver-installed     -       -       8K      2026-06-20 14:07
    zroot/ROOT/default@2026-06-20-14:07:44-0    -       -       2.68M   2026-06-20 14:07

15.0-i3-installed
  zroot/ROOT/15.0-i3-installed  -       -       278M    2026-06-20 13:35
    zroot/ROOT/default@2026-06-20-17:26:09-0    -       -       776K    2026-06-20 17:26

default
  zroot/ROOT/default    NR      /       10.1G   2026-06-21 09:08
  default@2026-06-20-13:16:09-0 -       -       2.96M   2026-06-20 13:16
  default@2026-06-20-13:29:05-0 -       -       12.2M   2026-06-20 13:29
  default@2026-06-20-14:07:44-0 -       -       2.68M   2026-06-20 14:07
  default@2026-06-20-17:26:09-0 -       -       776K    2026-06-20 17:26
  default@2026-06-21-07:41:50-0 -       -       1.02M   2026-06-21 07:41
  default@2026-06-21-08:01:21-0 -       -       1012K   2026-06-21 08:01
  default@2026-06-21-08:51:09-0 -       -       15.0M   2026-06-21 08:51
  default@2026-06-21-09:08:39-0 -       -       1.05M   2026-06-21 09:08

I guess the snapshots are all labeled default@timestamp? And then referenced that way in the various BE datasets (clones) built on them?

Code:
toddg@system76 ~ $ zpool status
  pool: zdata
 state: ONLINE
config:

        NAME                          STATE     READ WRITE CKSUM
        zdata                         ONLINE       0     0     0
          mirror-0                    ONLINE       0     0     0
            gpt/data1-1tb-ct1000.eli  ONLINE       0     0     0
            gpt/data2-1tb-ct1000.eli  ONLINE       0     0     0

errors: No known data errors

  pool: zroot
 state: ONLINE
config:

        NAME          STATE     READ WRITE CKSUM
        zroot         ONLINE       0     0     0
          nda0p4.eli  ONLINE       0     0     0

errors: No known data errors



Here are the links that helped me:
* https://vermaden.wordpress.com/2021/02/23/upgrade-freebsd-with-zfs-boot-environments/
* https://vermaden.wordpress.com/wp-c...zfs-boot-environments-reloaded-2018-11-15.pdf
* https://freebsdsoftware.org/blog/freebsd-boot-environments/
* https://forums.freebsd.org/threads/modern-setup-of-disks-geli-zfs.83995/
 
I can't see any point in creating a BE after installing the kernel.

IMO there a two reasonable approaches:

1) Make a backup BE and update the running system as you would on UFS. I understand freebsd-update does this by default on ZFS.

2) Create a new BE, mount it with "bectl mount", and then update that.

I've never used freebsd-update, I've always preferred scripted source installs - especially since etcupdate removed the need for manual merging. With source installs (2) eliminates the need to reboot into the new kernel before installing world (you just need to defer deleting libs until after the next boot). I expect freebsd-update supports the same approach.
 
There is many posts to read here... But if the question is how to make an upgrade, I can write here how I do. I'm always on ZFS root.

1) I disabled automatic ZFS BE creation in freebsd-update (see freebsd-update.conf(5), var CreateBootEnv). I did this because you have the creation of two BEs where one is enough. Moreover, the names are too long, and when you have to deal with them in command line, it's a pain. Don't forget that a BE takes more and more room as time passes. You cannot leave a BE eternally unless you have unlimited disk space.

2) I use sysutils/beadm, not bectl as long as it's for command line and not scripting. Mainly by habits, but also because of experiencing terrible bugs in the first versions of bectl and mostly, the syntax is simpler, with less pitfalls.

3) I believe there is no point to make a BE and upgrade into. I just make a BE before to upgrade and do the job in the current one, often 'default' (it's just a name you can change if needed).

4) As many people already said, I look for any third-party kmod I may load whether in /etc/rc.conf or in /boot/loader.conf and I comment out all these kinds of loading. I disable also the launch of Xorg or any DE.

5) I run freebsd-update upgrade ... or the suited pkg command for pkgbase, depending the type of your installation (don't mix them!).

6) If the installation isn't under pkgbase, I reboot to get the new kernel active. Then, I freebsd-update install the userland.

7) I upgrade the kmods: pkg upgrade -fr FreeBSD-ports-kmods. I check if all the kmods are really in sync with the current kernel (you see that in the name of the package). If you want to be double sure, you can kldload each kmod to see if a problem arise.

8) If it is a major upgrade: pkg upgrade -fr FreeBSD-ports. Plus the last freebsd-update install to remove the old libraries if aren't under pkgbase (note that sometimes, it's needed for a minor version upgrade, it's the case for 15.1-RELEASE).

9) Reactivate the loading of third-party kmods and Xorg (if needed). Reboot.

If something goes wrong, you can select the BE you made before in the beasty menu to return to the previous version. Then, you can beadm activate this BE to automatically reboot into. You can even rename this BE 'default' if you want.
 
I reboot to get the new kernel active.
Leaving the old boot environment marked as R — to be activated on reboot? What, then, replaces BE with the new one? And how is the R mark handled automatically in that case? Only this one question remains unclear to me, not BE management for backup purpose.
 
Sorry, gonna get wordy/long again.
NOTE: this is all based on my understanding of using freebsd-update and ZFS, the workflow works for and makes sense to me (kind of KISS principle), is based on default behavior and does not include operations on GELI systems.
It looks like each BE creates a dataset... that's cool, but I would have thought it'd also create a snapshot (see next):
The initial BE is a distinct dataset, every BE created after that is technically a "ZFS Clone" of that dataset. A ZFS snapshot is readonly, a ZFS clone is semantically "A writeable snapshot of a snapshot", so by definition a BE contains/utilizes a snapshot.

* What does `bectl` backup? How can I see what it's backing up?
*
A BE is a structured set of directories/datasets that live under root directory, aka "/" that do NOT have a separate dataset. Roughly (I may be missing things) it includes the directories:
/, /etc, /sbin, /bin, /usr/bin, /usr/sbin, /usr/local/..., /var/db plus subdirectories. zfs list shows you datasets and mount points. How can you see? Not sure, but "backing up" is not really what's going on.
* How do I backup my BE's? Instructions say to backup /var/backups... is that sufficient?
* How would I restore a BE from backup? Is that a thing one would want/need to do?
I've never "backed up my BEs" by definition a BE is a clone of a dataset that contains deltas from it's parent. So, to me backing up and restoring is kind of nonsensical.
* I just re-installed my OS several times in the last 2 days and I don't remember whether I used the traditional method or the newer method...how can I tell?
If you were reinstalling 15.x and accepted the defaults, I believe it defaults to "not pkgbase". As for how to tell? I think "pkg info | grep -i freebsd" may give a clue.
* Vermadden (link below) has instructions about dealing with boot loaders... Is this b/c of version upgrades, from say 15.0 to 15.1, or is this because of geli encrypted drives and zfs? How does bectl (and BEs) interact with boot loaders?
Bootloaders: Updating bootloader is tied to doing "zpool upgrade" on your root pool. If you do not do that, it is optional. Has nothing to do with versions/geli, but rather "what version is your zpool". Think compatibility. A bootloader from a year ago has knowledge of zfs pool features at that time, but not today. If you do updates and then zpool upgrade your zpool is now "today". If you do not update your bootloader to "today" before a reboot, the old boot loader does not understand today zpool features and will not boot. A "today" bootloader understands "yesterdays" features so updating bootloaders is usually safe.

Your Primary Question:
Yes, changes you make in your current BE are persistant across reboots.
gpt disk labeling is an interesting case.
Swap: if you do swapoff, then update /etc/fstab to use a gpt label, then swapon that should work and should be persisted.
Changing the name/label of the vdev used for a boot pool is different/harder and not something you can easily do on a live system unless you are at least mirrored.

GELI: that adds a layer of complexity I'm not 100% familiar with. My limited understanding is you create the GELI top level device, then gpart is applied to that, not the raw device.
So I really can't answer.

I'm confused as to why there are only zroot/ROOT/default snapshots here...
Because you were booted into the BE named "default" when freebsd-update install created the new BE.

My post back up at #4, the "key points about freebsd-update" describes the default behavior.

Honestly, walk through the steps in a VM, paying attention to names and the flags in the Active column for the bectl commands. For all the bectl commands the NR commands should be staying with the same BE.
Install 15.0-RELEASE, this should give you I think 15.0-RELEASE-p10
bectl list should show a single BE named "default"
freebsd-update upgrade -r 15.1-RELEASE


bectl list should show a single BE named "default"
freebsd-update install this installs the 15.1 kerne
bectl list should show 2 BEs, one named "default" the other "default" plus timestamp
freebsd-version -kru should show current kernel as 15.0, next kernel 15.1, userland 15.0
reboot
freebsd-version -kru should show current kernel as 15.1, next kernel as 15.1, userland as 15.0
bectl list should show 2 BEs, one named "default" the other "default" plus timestamp
freebsd-update install update userland
bectl list should show 2 BEs
freebsd-version -kru current kernel 15.1, next kernel 15.1, userland 15.1
freebsd-update install may not do anything but should clean out old libraries
bectl list should show 2 BEs one name default with NR flags one timestamped.

I can't see any point in creating a BE after installing the kernel.

IMO there a two reasonable approaches:

1) Make a backup BE and update the running system as you would on UFS. I understand freebsd-update does this by default on ZFS.

2) Create a new BE, mount it with "bectl mount", and then update that.
I've been using freebsd-update for a while (got tired of building from source) and as long as one understands and accepts the default behavior, it works just fine with ZFS.
freebsd-update install creates a new BE which is "the currently running BE before any updates are applied" so "my current BE will be updated".
As for these 2 specific points:
If I am updating within a release like 15.0-RELEASE-p5 to 15.0-RELEASE-p10 I let the tool do it's thing.
If I am doing a quarterly package upgrade for my applications, I typically create a new BE before upgrade so I can roll back quickly if something is broken.
Item 2: When I am upgrading across releases like 14.x-RELEASE to 15.x-RELEASE or 15.0-RELEASE to 15.1-RELEASE I manually create a new BE and do freebsd-update into it (freebsd-update and pkg commands have chroot options to enable this). Why? That means I can do all needed upgrad operations into the new BE, make sure it's consistent (base and packages) and reboot once into to.
bectl mount is powerful: you can use it to fix broken BEs (comment out things in kldlist) pretty easily.
bectl rename is another powerful command. I always rename my current BE to reflect what it actually contains (example from one of my systems, my current/default BE is running 15.0-RELEASE-p10, I have a 15.1-RELEASE-p0 created and marked as "use this next boot" (the T) flag.

bectl list
BE Active Mountpoint Space Created
15.0-RELEASE-p10 NR / 15.0G 2025-12-05 17:04
15.0-RELEASE-p9 - - 176M 2026-06-16 09:29
15.1-RELEASE-p0 T - 9.89G 2026-06-16 10:22
 
The "old BE" is where you did the upgrade, it's the current one. You will reboot on it by default, yes. You saved your precedent OS version in the BE you created just before to upgrade. If needed, you can select it to boot in the beasty menu, option 8.

Example:
% beadm list
BE Active Mountpoint Space Created
default NR / 28.7G 2025-10-26 10:52
au1 - - 202.0M 2026-06-10 17:46
au2 - - 173.0M 2026-06-18 18:50

I have currently two BEs.

Let's say, I have an upgrade to do.
# beadm delete au1
# beadm create au3


Then I do the upgrade (in 'default') and, if something goes wrong, I will select the 'au3' BE to return to the previous state.
 
So why is not the transition to the new boot environment offered officially, either automatically or manually? What is the point of staying in last year's BE status?
 
Yes, changes you make in your current BE are persistant across reboots.
Despite last year's BE date? If I understand correctly, then perhaps there is no contradiction between the official procedure and mine. Mine seems clearer to me, so I will stick to my own order. Thank you all for your advice and explanations!
 
Boot environments are kind of ZFS datasets — read-only snapshots by default. Archived for disaster recovery. System copies, intended for possible emergency reverting to its previous state.

However, the N marked BE becomes writable - current - working - live -active boot environment, just like a mounted ZFS dataset (thanks mer, Emrion, bob2112 for the hint). And by default it remains marked R too (NR) as an active BE on reboot during an upgrade.

That is the only thing I did not quite understand. Perhaps what confused me the most was the very old date of an active boot environment.
 
Back
Top