How freebsd-update Works

`freebsd-update` is a binary update tool for the FreeBSD base system only. It does not touch packages installed via pkg or ports. It fetches signed binary diffs from FreeBSD's update servers, verifies them with SHA-256 and RSA signatures, and applies them to `/`. The tool tracks the installed base via a metadata database in `/var/db/freebsd-update/`.

Unlike Linux tools such as apt or dnf, `freebsd-update` operates on the compiled base system. Kernel, world, and system libraries are all managed as a unit. This means a patch fetch may update `/boot/kernel/kernel`, `/lib/libc.so.7`, and `/usr/bin/ssh` in a single transaction.

The configuration file lives at `/etc/freebsd-update.conf`. The defaults are sane for most servers. The two settings worth reviewing immediately are `Components` and `BackupKernel`.

By default, `Components` includes `kernel src world`. If you do not need source code on the server, removing `src` reduces download size significantly. `BackupKernel` defaults to `yes`, which copies the running kernel to `/boot/kernel.old` before installing a new one - leave this enabled.

grep -E '^(Components|BackupKernel)' /etc/freebsd-update.conf
# Expected output:
# Components world kernel src
# BackupKernel yes

Applying Security Patches with freebsd-update fetch install

The standard patch cycle is two commands. `fetch` downloads and verifies available patches. `install` applies them. You should run `fetch` first to review what will change before committing.

After `fetch`, freebsd-update prints a summary of affected files. Examine this list. On production systems, changes to `/etc/ssl/cert.pem`, `/usr/lib/libcrypto.so.111`, or kernel modules warrant a closer look before installing.

If the kernel is patched, a reboot is required. If only userland binaries are updated, you may need to restart specific services rather than rebooting. For example, if `/usr/bin/ssh` and `sshd` libraries are updated, restart sshd: `service sshd restart`. The tool itself does not force a reboot for userland-only patches.

We run freebsd-update fetch on a cron schedule on non-production mirrors to detect available patches ahead of time. We do not automate `install` in cron - that step requires a human decision and, often, a maintenance window.

# Fetch available patches
freebsd-update fetch

# Review what will be changed
freebsd-update fetch | grep '^/' | head -40

# Apply patches
freebsd-update install

# Reboot if kernel was updated
reboot

Checking Current Release and Pending Patches

Before running any update, confirm exactly what version is installed and what the update tool sees.

`freebsd-version -ku` is the authoritative command here. The `-k` flag reports the running kernel version. The `-u` flag reports the userland version from `/bin/freebsd-version`. These can differ after a partial upgrade or if you forgot to reboot after a kernel update.

If the two versions differ, that is a signal to reboot before proceeding with further updates. Mismatched kernel and world versions are not dangerous on FreeBSD in most cases, but they can cause subtle issues with kernel modules that link against userland libraries.

The `/var/db/freebsd-update/` directory also contains `tag` and `version` files that record what freebsd-update last installed. Checking these against the live `uname -r` output helps diagnose situations where a patch was fetched but install was never run.

# Check kernel and userland versions
freebsd-version -ku

# Example output on a patched system:
# 14.1-RELEASE-p6  (kernel)
# 14.1-RELEASE-p6  (userland)

# Check uname for running kernel
uname -r

# Check freebsd-update internal state
cat /var/db/freebsd-update/tag
// advertisement

Minor Release Upgrade: 14.1 to 14.2

Minor upgrades within a major branch (13.3 to 13.4, 14.1 to 14.2) follow the same `freebsd-update` workflow but use the `-r` flag to specify the target release. This is generally low risk because the ABI is stable within a major branch.

The process has three phases: fetch, install the new userland and kernel, reboot, then run install again to handle any remaining steps like merging configuration files.

During the first `install` pass, freebsd-update may invoke `mergemaster` or `etcupdate` to handle `/etc` changes. On FreeBSD 14.x the default is `etcupdate`. Pay attention to conflicts in `/etc/passwd`, `/etc/group`, and `/etc/rc.conf` - the tool marks these and requires manual resolution.

After the reboot, run `freebsd-update install` a second time. This finalizes the upgrade and removes obsolete files. Check for obsolete packages after the base upgrade: `pkg upgrade` may need to rebuild ports that link against updated system libraries.

# Upgrade from 14.1 to 14.2-RELEASE
freebsd-update -r 14.2-RELEASE upgrade

# First install pass - installs new kernel
freebsd-update install

# Reboot into new kernel
shutdown -r now

# After reboot - second install pass for userland
freebsd-update install

# Upgrade packages against new libraries
pkg upgrade

# Third install pass if prompted (removes old files)
freebsd-update install

Major Release Upgrade: FreeBSD 13.x to 14.x

Major version upgrades carry more risk because ABI changes between major branches can break pkg-installed software. We upgraded three test servers from 13.4 to 14.1 to document this process.

Before starting, snapshot your ZFS pool or take a VM snapshot. On ZFS systems, this is one command and costs nothing: `zfs snapshot -r zroot@pre-upgrade-13.4`. This gives you a clean rollback point if the upgrade breaks something critical.

The command syntax is identical to a minor upgrade - only the target release number changes. The key difference is that `freebsd-update` will download significantly more data for a major upgrade. On our test server, 13.4 to 14.1 fetched approximately 340 MB.

After the first install pass and reboot, verify you are running the new kernel with `uname -r` before proceeding. We had one case where the boot loader selected the old kernel because `/boot/loader.conf` had a hardcoded kernel path. Check your loader configuration if `uname -r` still shows the old release.

Post-upgrade, run `pkg-static upgrade -f` to force reinstall all packages against the new ABI. The `-f` flag reinstalls even packages that appear current - this is important because shared library versions may have changed under the hood. Do not skip this step.

# Snapshot ZFS before upgrade
zfs snapshot -r zroot@pre-upgrade-13.4

# Start major upgrade
freebsd-update -r 14.1-RELEASE upgrade

# Install new kernel first
freebsd-update install

# Reboot
shutdown -r now

# Verify new kernel
uname -r
# Should show: 14.1-RELEASE

# Install new userland
freebsd-update install

# Force reinstall all packages
pkg-static upgrade -f

# Final cleanup pass
freebsd-update install

Handling etcupdate Conflicts

The most common point of failure in upgrades is `/etc` file conflicts. FreeBSD 14.x uses `etcupdate` to manage base system configuration files during upgrades. When freebsd-update calls etcupdate and finds local modifications, it flags conflicts and writes them to `/var/db/etcupdate/conflicts/`.

The workflow for resolving conflicts: run `etcupdate resolve` to enter an interactive merge session for each conflicted file. The tool shows a three-way diff between the original base, your local version, and the new base version. For most files you will accept the new version. For files like `/etc/ssh/sshd_config`, `/etc/pf.conf`, or custom `/etc/rc.conf` fragments, review carefully.

After resolving, run `etcupdate diff` to verify no outstanding conflicts remain. A clean `etcupdate diff` with no output means you are clear to proceed.

Do not ignore etcupdate conflicts and reboot anyway. Unresolved conflicts mean `/etc/passwd` or `/etc/group` may be missing new system accounts added in the new release. This can cause service failures or silent security issues.

# List conflicts after freebsd-update install
etcupdate diff

# Resolve conflicts interactively
etcupdate resolve

# After resolution, confirm clean state
etcupdate diff
# No output = no conflicts remaining

# If you want to review what etcupdate changed
etcupdate status
// advertisement

Verifying File Integrity After Updates

`freebsd-update IDS` (Intrusion Detection System mode) compares the installed base system files against the checksums from the official FreeBSD build. This is useful both after upgrades and as a routine integrity check.

The output lists files whose SHA-256 hash differs from the official release. Legitimate differences will appear for files you have intentionally modified, like `/etc/ssh/sshd_config` or `/etc/pf.conf`. Unexpected differences in binaries like `/bin/sh`, `/sbin/pfctl`, or anything under `/usr/lib/` warrant investigation.

We run `freebsd-update IDS` as part of our post-upgrade checklist on every server. The full scan takes 30-90 seconds depending on disk speed. Pipe through grep to filter out expected `/etc` modifications and focus on binary paths.

Note: IDS mode compares against the currently installed release version. Run it after completing the full upgrade and cleanup pass, not in the middle of the process.

# Full integrity check
freebsd-update IDS

# Filter to show only binary/library changes, skip /etc
freebsd-update IDS | grep -v '^/etc/' | grep -v '^/usr/share/'

# Count total modified files
freebsd-update IDS | wc -l

Automating Patch Fetching Without Auto-Installing

Many operations teams want automated detection of available patches combined with manual install approval. `freebsd-update cron` is the correct mode for this. It runs fetch in the background, sends mail if patches are found, and exits without installing anything.

Add this to root's crontab or `/etc/cron.d/`. The `cron` subcommand adds a random sleep of 0-3600 seconds to spread load on the update servers, which matters if you manage a fleet of machines.

For teams building automated patch pipelines, this output can feed into a notification or ticketing system. If you are managing update workflows as part of a broader DevOps automation stack, tools like those at taskbotshub.ai can ingest this kind of event and trigger approval workflows or maintenance window scheduling without manual cron babysitting.

Never put `freebsd-update install` in cron on a production server unless you have a robust snapshot and rollback strategy and have tested your recovery procedure. We have seen kernel updates applied automatically that required a reboot, leaving the system running the old kernel while the new userland was installed - a mismatch that persisted for days unnoticed.

# Add to /etc/crontab or crontab -e for root
# Fetch patches nightly, mail root if found
@daily root freebsd-update cron

# To configure mail recipient, set in /etc/freebsd-update.conf
MailTo root

# Test cron mode manually (adds random delay, use -s to skip)
freebsd-update -s update6.freebsd.org cron

Rollback After a Failed Upgrade

FreeBSD does not have a built-in `freebsd-update rollback` command for full version downgrades. Rollback strategies depend on what you had in place before the upgrade.

For ZFS users, the snapshot approach is the reliable path. If the upgrade broke something critical, rollback with `zfs rollback` and you are back to the pre-upgrade state in seconds. The snapshot must have been taken before the upgrade, and on a live system you will need to boot from a rescue environment to roll back the root pool.

For UFS systems or VMs, the options are a pre-upgrade disk snapshot (hypervisor-level), a backup restore, or - in desperation - reinstalling the target release and re-running freebsd-update to get back to a clean known state.

There is one limited rollback available: if freebsd-update installed a bad kernel and the system fails to boot, FreeBSD's boot loader menu offers the `kernel.old` option at boot time. Select `Escape to loader prompt` and run `unload; boot /boot/kernel.old/kernel`. This gets you to userland running the previous kernel, from which you can diagnose or revert.

On our test servers, every pre-production upgrade runs against a VM clone first. The actual upgrade procedure is scripted, runs against the clone, and produces a pass/fail result before we touch production. This is not optional for machines serving traffic.

# ZFS rollback (run from rescue environment for root pool)
zfs rollback zroot@pre-upgrade-13.4

# Boot old kernel from loader prompt (at boot menu)
# Press Escape, then at loader> prompt:
unload
boot /boot/kernel.old/kernel

# List available ZFS snapshots before deciding
zfs list -t snapshot -r zroot
// advertisement

freebsd-update on Jails

Updating jail base systems requires separate `freebsd-update` invocations with the `-b` flag pointing at the jail root. Each jail that uses the base system independently (not a NullFS mount from the host) must be updated individually.

For jails sharing the host base via NullFS read-only mounts, updating the host base updates all jails simultaneously - no separate action needed. Verify your jail configuration to confirm which model you are using before assuming.

The `-b` flag sets the basedir for the operation. The jail does not need to be running for this to work, which matters for offline maintenance. After updating, restart the jail to pick up any library changes.

For large jail fleets, scripting this loop is straightforward. The critical part is that each jail must be running the same FreeBSD release as the host, or the same release as whatever base tarball it was built from. Running a 13.x jail base on a 14.x host kernel is an officially unsupported configuration as of FreeBSD 14.

# Update a specific jail's base system
freebsd-update -b /usr/jails/myjail fetch install

# Update all jails in /usr/jails
for jail in /usr/jails/*/; do
  echo "Updating ${jail}"
  freebsd-update -b "${jail}" fetch install
done

# Restart updated jails
service jail restart myjail

Common Errors and What They Actually Mean

The error `No mirrors remaining, giving up` during fetch means DNS resolution for `update.freebsd.org` failed, your firewall is blocking port 80 or 443 outbound, or FreeBSD's update servers are temporarily unavailable. Check with `dig update.freebsd.org` and `curl -v http://update.freebsd.org/` before assuming the tool is broken.

The message `src component not installed, skipping` is not an error. It means `src` is listed in `Components` in your config but the source tree was never installed. Remove `src` from the Components line in `/etc/freebsd-update.conf` if you do not use it.

If `freebsd-update install` exits with `No updates are available to install` but you know patches were fetched, check that you are running as root and that the `/var/db/freebsd-update/` directory is intact. Stale lock files in that directory can also cause this.

The warning `Modified configuration file detected` during a major upgrade means etcupdate found local changes. This is expected and not an error - follow the etcupdate resolve workflow described earlier.

On one of our test servers we saw `freebsd-update: Cannot open /var/db/freebsd-update/`: this happened because a previous failed fetch had left the directory in an inconsistent state. The fix was `rm -rf /var/db/freebsd-update/` and starting fresh with `freebsd-update fetch`. You lose tracking history but the tool rebuilds it from scratch correctly.

# Debug DNS resolution for update servers
dig update.freebsd.org

# Test HTTP connectivity to update servers
curl -v --max-time 10 http://update.freebsd.org/

# Clear corrupted freebsd-update state (last resort)
rm -rf /var/db/freebsd-update/
freebsd-update fetch