⬆️ Upgrading FreeBSD Safely: freebsd-update, pkg and Boot Environments

A tested walkthrough of a patch update and a full release upgrade (15.0 to 15.1) on a real FreeBSD system, with an undo button you actually tried

Video: Upgrading FreeBSD Safely (FreeBSD video)
Prefer to watch? This short video runs this page's main commands on a real FreeBSD 15.0 machine, with on-screen captions and no voiceover. Click to play, or watch it on YouTube.

The plan

Upgrading FreeBSD is calmer than it sounds, because the system gives you good tools. There are three layers, each with its own command:

  1. The base system (kernel and core tools): updated with freebsd-update.
  2. Software you installed (/usr/local): updated with pkg.
  3. The safety net: a boot environment, a bootable copy of the whole system, made with bectl (this works because the install uses ZFS, see the overview page, FreeBSD vs Linux).

And two sizes of upgrade:

We did both, in this order, on a real FreeBSD 15.0-RELEASE virtual machine that was a fresh install. Download counts, times and file names on your machine will differ. Every screen below is real, and where output is very long, a note says what was left out.

Step 0: where are you now?

root@practice:~ # freebsd-version -kru
15.0-RELEASE
15.0-RELEASE
15.0-RELEASE
root@practice:~ # bectl create pre-update
root@practice:~ # bectl list
BE         Active Mountpoint Space Created
default    NR     /          458M  2026-10-05 20:58
pre-update -      -          8K    2026-10-05 21:52

freebsd-version -kru prints three numbers (see the overview page): the kernel installed on disk, the running kernel and the userland. On a fresh install all three say 15.0-RELEASE. Keep an eye on the three numbers throughout: they show you exactly how far an upgrade has got.

Step 1: make the undo button

Before changing anything, create a boot environment. In the block above, bectl create pre-update made a copy of the running system that takes only 8K of space, because ZFS stores just the differences from now on, and bectl list shows it next to default. This costs nothing and takes a second. If anything goes wrong later, you can boot back into it. (You will see this done for real in the rollback section below.)

Step 2: fetch the update

root@practice:~ # freebsd-update fetch
src component not installed, skipped
Looking up update.FreeBSD.org mirrors... 3 mirrors found.
Fetching public key from update2.freebsd.org... done.
Fetching metadata signature for 15.0-RELEASE from update2.freebsd.org... done.
Fetching metadata index... done.
Fetching 2 metadata files... done.
Inspecting system... done.
Preparing to download files... done.
Fetching 1734 patches.....10....20....30....40....50 [progress dots shortened]
Applying patches... done.
Fetching 12 files... ....10. done.
The following files will be removed as part of updating to
15.0-RELEASE-p14:
/usr/include/openssl/conf_api.h
The following files will be added as part of updating to
15.0-RELEASE-p14:
/usr/include/openssl/der_digests.h
/usr/include/openssl/der_dsa.h
/usr/include/openssl/der_ec.h
/usr/include/openssl/der_ecx.h
/usr/include/openssl/der_ml_dsa.h
/usr/include/openssl/der_rsa.h
/usr/include/openssl/der_slh_dsa.h
[... thousands of file names left out to keep this page short ...]
/usr/share/zoneinfo/zone.tab
/usr/share/zoneinfo/zone1970.tab
/usr/tests/sys/vm/stack/libsoxstack.so
/var/db/etcupdate/current/etc/mtree/BSD.tests.dist

WARNING: FreeBSD 15.0-RELEASE HAS PASSED ITS END-OF-LIFE DATE.
Any security issues discovered after Thu Oct  1 00:00:00 UTC 2026
will not have been corrected.

freebsd-update fetch talks to the FreeBSD update servers, checks their signature, compares your installed files with the official ones and downloads the differences. It changes nothing on your system yet. Reading the output:

Step 3: install, in two passes

root@practice:~ # freebsd-update install
src component not installed, skipped
Creating snapshot of existing boot environment... done.
Installing updates...
Restarting sshd after upgrade
Performing sanity check on sshd configuration.
Stopping sshd.
Waiting for PIDS: 1177.
Performing sanity check on sshd configuration.
Starting sshd.
 done.
root@practice:~ # freebsd-version -kru
15.0-RELEASE-p14
15.0-RELEASE
15.0-RELEASE-p14
root@practice:~ # bectl list
BE                             Active Mountpoint Space Created
15.0-RELEASE_2026-10-05_215829 -      -          352K  2026-10-05 21:58
default                        NR     /          912M  2026-10-05 20:58
pre-update                     -      -          188K  2026-10-05 21:52
root@practice:~ # shutdown -r now
Shutdown NOW!
shutdown: [pid 82917]

freebsd-update install makes the changes real. Notice what happened by itself:

root@practice:~ # freebsd-version -kru
15.0-RELEASE-p14
15.0-RELEASE-p14
15.0-RELEASE-p14
root@practice:~ # freebsd-update install
src component not installed, skipped
No updates are available to install.
Run 'freebsd-update [options] fetch' first.
root@practice:~ # freebsd-version -kru
15.0-RELEASE-p14
15.0-RELEASE-p14
15.0-RELEASE-p14
root@practice:~ # uname -a
FreeBSD practice 15.0-RELEASE-p14 FreeBSD 15.0-RELEASE-p14 releng/15.0-n281126-2b7df8c16dc7 GENERIC amd64
root@practice:~ # uptime
10:01PM  up 1 min, 1 user, load averages: 0.35, 0.10, 0.03

After the reboot, all three numbers say 15.0-RELEASE-p14, and uname -a shows the patch level in its first line. A second freebsd-update install now says No updates are available to install, so for a patch update one pass plus one reboot was enough.

root@practice:~ # freebsd-update fetch
src component not installed, skipped
Looking up update.FreeBSD.org mirrors... 3 mirrors found.
Fetching metadata signature for 15.0-RELEASE from update2.freebsd.org... done.
Fetching metadata index... done.
Inspecting system... done.
Preparing to download files... done.

No updates needed to update system to 15.0-RELEASE-p14.

WARNING: FreeBSD 15.0-RELEASE-p14 HAS PASSED ITS END-OF-LIFE DATE.
Any security issues discovered after Thu Oct  1 00:00:00 UTC 2026
will not have been corrected.

Fetching again confirms No updates needed to update system to 15.0-RELEASE-p14. (The end-of-life warning is still there, because the patch level does not change the release.)

The undo button, tested

An undo button that you have never pressed is only a hope. So we pressed it. bectl activate pre-update tells the boot loader to start the old copy next time:

root@practice:~ # bectl list
BE                             Active Mountpoint Space Created
15.0-RELEASE_2026-10-05_215829 -      -          440K  2026-10-05 21:58
default                        NR     /          912M  2026-10-05 20:58
pre-update                     -      -          188K  2026-10-05 21:52
root@practice:~ # bectl activate pre-update
Successfully activated boot environment pre-update
root@practice:~ # bectl list
BE                             Active Mountpoint Space Created
15.0-RELEASE_2026-10-05_215829 -      -          440K  2026-10-05 21:58
default                        N      /          454M  2026-10-05 20:58
pre-update                     R      -          458M  2026-10-05 21:52
root@practice:~ # shutdown -r now
Shutdown NOW!

Read the Active column: N means "active Now", R means "active on Reboot", and NR means both. After bectl activate, default is N (still running) and pre-update is R (will start next). Then we rebooted:

root@practice:~ # freebsd-version -kru
15.0-RELEASE
15.0-RELEASE
15.0-RELEASE
root@practice:~ # bectl list
BE                             Active Mountpoint Space Created
15.0-RELEASE_2026-10-05_215829 -      -          440K  2026-10-05 21:58
default                        -      -          454M  2026-10-05 20:58
pre-update                     NR     /          458M  2026-10-05 21:52
root@practice:~ # bectl activate default
Successfully activated boot environment default
root@practice:~ # shutdown -r now

The machine came back as plain 15.0-RELEASE, the state before the update, with pre-update now NR. The whole update was undone by one command and one reboot, with no restore from backup. We then activated default again and rebooted, and the system returned to 15.0-RELEASE-p14 (we checked: freebsd-version said 15.0-RELEASE-p14 again, and the release upgrade below started from there).

Remember: a boot environment holds the operating system (the root dataset). Your data under /home, /var/log and so on are separate datasets and are not rolled back. That is usually what you want, but it is not a replacement for backups.

Step 4: your packages with pkg

⚠️ Newer package repository (checked October 2026): the package repository now targets FreeBSD 15.1, while 15.0-RELEASE is end of life. On a 15.0 machine pkg may stop with Newer FreeBSD version for package ... [y/N], and the version numbers in the output below may differ from yours. To carry on, run export IGNORE_OSVERSION=yes first, or better, move to a supported release (see the upgrading guide).

root@practice:~ # export IGNORE_OSVERSION=yes
root@practice:~ # pkg -v
The package management tool is not yet installed on your system.
Do you want to fetch and install it now? [y/N]: y
Bootstrapping pkg from pkg+https://pkg.FreeBSD.org/FreeBSD:15:amd64/quarterly, please wait...
Verifying signature with trusted certificate pkg.freebsd.org.2013102301... done
Installing pkg-2.8.4...
Extracting pkg-2.8.4: 100%
2.8.4
root@practice:~ # pkg update
Updating FreeBSD-ports repository catalogue...
Fetching meta.conf: 100%     168 B   0.2 kB/s    00:01
Fetching data: 100%  9296 KiB   4.8 MB/s    00:02
Processing entries: 100%
FreeBSD-ports repository update completed. 38288 packages processed.
Updating FreeBSD-ports-kmods repository catalogue...
Fetching meta.conf: 100%     179 B   0.2 kB/s    00:01
Fetching data: 100%    35 KiB  35.9 kB/s    00:01
Processing entries: 100%
FreeBSD-ports-kmods repository update completed. 240 packages processed.
All repositories are up to date.
root@practice:~ # pkg install nano
Updating FreeBSD-ports repository catalogue...
FreeBSD-ports repository is up to date.
Updating FreeBSD-ports-kmods repository catalogue...
FreeBSD-ports-kmods repository is up to date.
All repositories are up to date.
Updating database digests format: 100%
The following 3 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
	gettext-runtime: 1.0_1 [FreeBSD-ports]
	indexinfo: 0.3.1_1 [FreeBSD-ports]
	nano: 9.2 [FreeBSD-ports]

Number of packages to be installed: 3

The process will require 5 MiB more space.
1 MiB to be downloaded.

Proceed with this action? [y/N]: y
[1/3] Fetching indexinfo-0.3.1_1: 100%    6045 B   6.1 kB/s    00:01
[2/3] Fetching nano-9.2: 100%   698 KiB 714.5 kB/s    00:01
[3/3] Fetching gettext-runtime-1.0_1: 100%   301 KiB 308.1 kB/s    00:01
Checking integrity... done (0 conflicting)
[1/3] Installing indexinfo-0.3.1_1...
[1/3] Extracting indexinfo-0.3.1_1: 100%
[2/3] Installing gettext-runtime-1.0_1...
[2/3] Extracting gettext-runtime-1.0_1: 100%
[3/3] Installing nano-9.2...
[3/3] Extracting nano-9.2: 100%
root@practice:~ # which nano
/usr/local/bin/nano
root@practice:~ # pkg info
gettext-runtime-1.0_1          GNU gettext runtime libraries and programs
indexinfo-0.3.1_1              Utility to regenerate the GNU info page index
nano-9.2                       Nano's ANOther editor, an enhanced free Pico clone
pkg-2.8.4                      Package manager
root@practice:~ # pkg audit -F
Fetching vuln.xml.xz: 100%  1313 KiB   1.3 MB/s    00:01
0 problem(s) in 0 package(s) found.
root@practice:~ # pkg upgrade
Updating FreeBSD-ports repository catalogue...
FreeBSD-ports repository is up to date.
Updating FreeBSD-ports-kmods repository catalogue...
FreeBSD-ports-kmods repository is up to date.
All repositories are up to date.
Checking for upgrades (1 candidates): 100%
Processing candidates (1 candidates): 100%
Checking integrity... done (0 conflicting)
Your packages are up to date.
root@practice:~ # pkg autoremove -n
Checking integrity... done (0 conflicting)
Nothing to do.
root@practice:~ # pkg clean -n
Nothing to do.

Compare with Ubuntu (lesson 6 of Linux 101): pkg update ~ apt update, pkg upgrade ~ apt upgrade, pkg audit has no direct apt twin.

Release upgrade: 15.0 to 15.1

Now the bigger job. First check the machine is healthy and on the latest patch level (step 3), and make a boot environment with a name you will recognise:

root@practice:~ # bectl create before-15.1
root@practice:~ # bectl list
BE                             Active Mountpoint Space Created
15.0-RELEASE_2026-10-05_215829 -      -          616K  2026-10-05 21:58
before-15.1                    -      -          8K    2026-10-05 22:14
default                        NR     /          981M  2026-10-05 20:58
pre-update                     -      -          468K  2026-10-05 21:52
root@practice:~ # freebsd-update upgrade -r 15.1-RELEASE
src component not installed, skipped
Looking up update.FreeBSD.org mirrors... 3 mirrors found.
Fetching metadata signature for 15.0-RELEASE from update2.freebsd.org... done.
Fetching metadata index... done.
Fetching 1 metadata files... done.
Inspecting system... done.

The following components of FreeBSD seem to be installed:
kernel/generic world/base

The following components of FreeBSD do not seem to be installed:
kernel/generic-dbg world/base-dbg world/lib32 world/lib32-dbg

Does this look reasonable (y/n)? y

Fetching metadata signature for 15.1-RELEASE from update2.freebsd.org... done.
Fetching metadata index... done.
Fetching 1 metadata patches. done.
Applying metadata patches... done.
Fetching 1 metadata files... done.
Inspecting system... done.
Preparing to download files... done.
Fetching 3192 patches.....10....20....30....40 [progress dots shortened]
Applying patches... done.
Fetching 808 files... ....10....20....30....40 [progress dots shortened]
The following files will be removed as part of updating to
15.1-RELEASE-p4:
/boot/firmware/iwm8000C
/etc/ssl/certs/02265526.0
/etc/ssl/certs/106f3e4d.0
/etc/ssl/certs/228f89db.0
[... thousands of file names left out to keep this page short ...]
To install the downloaded upgrades, run 'freebsd-update [options] install'.

The install has three passes. Pass 1 installs the new kernel only:

root@practice:~ # freebsd-update install
src component not installed, skipped
Creating snapshot of existing boot environment... done.
Installing updates...
Kernel updates have been installed.  Please reboot and run
'freebsd-update [options] install' again to finish installing updates.
root@practice:~ # freebsd-version -kru
15.1-RELEASE-p4
15.0-RELEASE-p14
15.0-RELEASE-p14
root@practice:~ # shutdown -r now
Shutdown NOW!
shutdown: [pid 27146]

Look at the three numbers: 15.1-RELEASE-p4 kernel on disk, but 15.0-RELEASE-p14 still running and for the userland. FreeBSD says it plainly: Kernel updates have been installed. Please reboot and run 'freebsd-update install' again. After the reboot, pass 2 installs the userland, and the packages are brought up to date, and pass 3 finishes the job:

root@practice:~ # freebsd-version -kru
15.1-RELEASE-p4
15.1-RELEASE-p4
15.0-RELEASE-p14
root@practice:~ # uname -a
FreeBSD practice 15.1-RELEASE-p4 FreeBSD 15.1-RELEASE-p4 releng/15.1-n283627-e38a7085f3a8 GENERIC amd64
root@practice:~ # freebsd-update install
src component not installed, skipped
Creating snapshot of existing boot environment... done.
Installing updates...
Restarting sshd after upgrade
Performing sanity check on sshd configuration.
Stopping sshd.
Waiting for PIDS: 1185.
Performing sanity check on sshd configuration.
Starting sshd.

Completing this upgrade requires removing old shared object files.
Please rebuild all installed 3rd party software (e.g., programs
installed from the ports tree) and then run
'freebsd-update [options] install' again to finish installing updates.
root@practice:~ # freebsd-version -kru
15.1-RELEASE-p4
15.1-RELEASE-p4
15.1-RELEASE-p4
root@practice:~ # pkg upgrade
Updating FreeBSD-ports repository catalogue...
FreeBSD-ports repository is up to date.
Updating FreeBSD-ports-kmods repository catalogue...
pkg: Repository FreeBSD-ports-kmods has a wrong packagesite, need to re-create database
Fetching meta.conf: 100%     179 B   0.2 kB/s    00:01
Fetching data: 100%    35 KiB  35.6 kB/s    00:01
Processing entries: 100%
FreeBSD-ports-kmods repository update completed. 239 packages processed.
All repositories are up to date.
Checking for upgrades (1 candidates): 100%
Processing candidates (1 candidates): 100%
Checking integrity... done (0 conflicting)
Your packages are up to date.
root@practice:~ # freebsd-update install
src component not installed, skipped
Creating snapshot of existing boot environment... done.
Installing updates... done.

All three numbers now say 15.1-RELEASE-p4.

⚠️ When freebsd-update asks you to merge: if you have edited a system config file (for example /etc/ssh/sshd_config) and the new release changed it too, freebsd-update opens a merge in your editor. You get vi unless you set EDITOR. If you meet this, the lesson on text editors in Linux 101 (nano, and how to leave vi) is the thing to read first: export EDITOR=ee or export EDITOR=nano before you start (from the documentation, not run here, because our test machine had no edited config files).

After the upgrade: check and tidy

root@practice:~ # freebsd-version -kru
15.1-RELEASE-p4
15.1-RELEASE-p4
15.1-RELEASE-p4
root@practice:~ # bectl list
BE                                 Active Mountpoint Space Created
15.0-RELEASE-p14_2026-10-05_222458 -      -          740K  2026-10-05 22:24
15.0-RELEASE_2026-10-05_215829     -      -          616K  2026-10-05 21:58
15.1-RELEASE-p4_2026-10-05_222747  -      -          772K  2026-10-05 22:27
15.1-RELEASE-p4_2026-10-05_223137  -      -          1.14M 2026-10-05 22:31
before-15.1                        -      -          1.29M 2026-10-05 22:14
default                            NR     /          1.73G 2026-10-05 20:58
pre-update                         -      -          468K  2026-10-05 21:52
root@practice:~ # freebsd-update fetch
src component not installed, skipped
Looking up update.FreeBSD.org mirrors... 3 mirrors found.
Fetching metadata signature for 15.1-RELEASE from update2.freebsd.org... done.
Fetching metadata index... done.
Fetching 2 metadata patches.. done.
Applying metadata patches... done.
Fetching 2 metadata files... done.
Inspecting system... done.
Preparing to download files... done.

No updates needed to update system to 15.1-RELEASE-p4.
root@practice:~ # zfs list -r -o name,used zroot/ROOT
NAME                                            USED
zroot/ROOT                                     1.73G
zroot/ROOT/15.0-RELEASE-p14_2026-10-05_222458     8K
zroot/ROOT/15.0-RELEASE_2026-10-05_215829         8K
zroot/ROOT/15.1-RELEASE-p4_2026-10-05_222747      8K
zroot/ROOT/15.1-RELEASE-p4_2026-10-05_223137      8K
zroot/ROOT/before-15.1                            8K
zroot/ROOT/default                             1.73G
zroot/ROOT/pre-update                           288K
root@practice:~ # bectl destroy -o pre-update
root@practice:~ # bectl destroy -o 15.0-RELEASE_2026-10-05_215829
root@practice:~ # bectl list
BE                                 Active Mountpoint Space Created
15.0-RELEASE-p14_2026-10-05_222458 -      -          740K  2026-10-05 22:24
15.1-RELEASE-p4_2026-10-05_222747  -      -          772K  2026-10-05 22:27
15.1-RELEASE-p4_2026-10-05_223137  -      -          1.25M 2026-10-05 22:31
before-15.1                        -      -          4.13M 2026-10-05 22:14
default                            NR     /          1.61G 2026-10-05 20:58

A checklist for a real server

A sysadmin's upgrade plan:<br>
Step 1: make a backup.<br>
Step 2: make another backup, in case the first one is the "backup".<br>
Step 3: on FreeBSD, there is no step 3. It is a boot environment.

🧪 Try it yourself

In a FreeBSD virtual machine (never first on the production box):

1. Show the three versions with freebsd-version -kru. 2. Create a boot environment called practice, list it and destroy it with bectl destroy -o practice. 3. Run freebsd-update fetch and read what it says. If there is something to install, create a boot environment first, then run freebsd-update install. 4. Reboot, and check the three numbers again. 5. Activate the old boot environment, reboot, check the numbers, then activate default again and reboot. 6. Bootstrap pkg, install a program, run pkg audit -F, and find where the program was installed (which).

The cheat sheet

← FreeBSD vs Linux: What Is Actually Different · ↑ FreeBSD Corner · ZFS in Practice: Pools, Datasets, Snapshots and Self-Healing →