Now that PkgBase is really supported, it is time to revisit my pkgbase use for the host and jails.

The official release of FreeBSD 15.1 was 2026-06-15. My systems have been using PkgBase for over a year (starting with FreeBSD-14.2), and I've been happy with it. I had already updated my system to FreeBSD 15.1 RC3, the upgrade of the host was as simple as
pkg -r /tmp/beupg151 upgrade -r FreeBSD-base
With the release of FreeBSD 15.0 I had set out to convert my jails to use packages instead of the big tarball. Works fine ever since! Patch updates all flawless.
NOTE: This is not a guide for converting your freebsd-update system to use PkgBase.
Boot-environments improvement
An improvement (or lack of knowledge on my part), running bectl mount FreeBSD-15.1-RC3 creates a temporary directory for you.
Before, I would
TMPDIR=$(mktemp -d)
bectl mount FreeBSD-15.1-RC3 ${TMPDIR}
pkg -r ${TMPDIR} upgrade ...
Now, you can use
TMPDIR=$(bectl mount FreeBSD-15.1-RC3)
pkg -r ${TMPDIR} upgrade ...
Base package "sets"
Not sure when they were introduced, but now there are "sets" in FreeBSD base packages. These are meta-packages that merely depend on other packages, simplifying installs.
If you are like me and don't want to install what you do not need, you can use FreeBSD-set-minimal for your system.
For jails you can use FreeBSD-set-minimal-jail.
Finding out what "sets" a package is in can be accomplished by looking at the files in /usr/src/release/packages/sets.
Pulling the trigger on the gun I had expertly loaded
I was lucky that I was using boot-environments, the system was running (as were services) but I could not login!
Somewhere after FreeBSD 15.0 release, I decided that I could minimize installs. Blissfully unaware of the "sets" at the time. I ended up with a system with a curated list of base packages I needed, and nothing more. At the time, I recall mentioning on IRC that some of the packages were a bit big, or contained many things I didn't need on my system. This came back to bite me.
Self-inflicted pain, I did not read the Release Notes.
Particulary the System Packaging Changes section.
Both PAM and zstd have been removed from FreeBSD-utilities, my system was running but I had no way to login.
The boot-environment was marked "temporary", so a reboot of the system landed me back in FreeBSD 15.0.
Fixed by adding FreeBSD-pam and FreeBSD-zstd to the boot environment.
pkg -r ${TMPDIR} install -r FreeBSD-base FreeBSD-pam FreeBSD-zstd
bectl activate -t ${TMPDIR}
shutdown -r now
Minimizing the base system
The easiest way to minimize your base system, you can use FreeBSD-set-minimal.
I've found these too large still (a server does not need Wifi firmware blobs or WPA), so I rolled my own. So installed FreeBSD-set-minimal, and then delete what I don't want.
pkg install FreeBSD-set-minimal
pkg delete -f FreeBSD-firmware-iwm FreeBSD-wpa FreeBSD-powerd
I may be setting myself up for failure later... Must remember to read the release notes!
Thin-provisioned jails
For creating jails use ezjail out of old habit.
For easier roll-back I started using ezjail_jailbase and the basejail mount in the respective /etc/fstab.<jailname>.
Reuse is still high enough to keep doing this.
What I ended up with were base jails like /jails/basefree150 and /jails/baselibre142.
Currently, I have 2 distinct base jails:
- set-base-jail-151, for maintaining ports and for poudriere,
- minimized-151, for all other jails.
An additional ZFS dataset zroot/jails/base was created for storing them.
At any time there can be multiple versions or test jails in there.
This separates the jails from the basejails with easier overview.
A new base jail is created as a ZFS dataset.
The required directories for packages are created, null-mounts are created from host to the basejail dir.
The other pkg commands are run from the host system.
Let's create the standard-151 basejail.
# Storage and required directories
ZFSPOOL=zroot
JAILSROOT=/jails
BASEJAIL=set-base-jail-151
zfs create ${ZFSPOOL}${JAILSROOT}/base/${BASEJAIL}
mkdir -p ${JAILSROOT}/base/${BASEJAIL}/var/cache/pkg
mkdir -p ${JAILSROOT}/base/${BASEJAIL}/var/db/pkg/repos
# Mount the package cache and repositories in the basejail
mount_nullfs /var/cache/pkg ${JAILSROOT}/base/${BASEJAIL}/var/cache/pkg
mount_nullfs /var/db/pkg/repos ${JAILSROOT}/base/${BASEJAIL}/var/db/pkg/repos
# Install
pkg -r ${JAILSROOT}/base/set-base-jail-151 install -r FreeBSD-base FreeBSD-set-base-jail
# 188 packages are installed
Now I can switch jails using the 15.0 "full" base jail to the 15.1 one one-by-one.
The base jail mount for a jail can be in either /etc/jail.conf.d/<jailname>.conf or in /etc/fstab.<jailname>.
Modify the line that nullfs mounts /jails/<jailname>/basejail into /jails/base/set-default-jail-151 and restart the jail.
sed -i.bak 's|/jails/basefree150|/jails/base/set-default-jail-151|' /etc/fstab.porting
service ezjail restart porting
Rinse-and-repeat for other jails using this base jail...
What packages do I need for my jails
Most of my jails don't need a complete base system.
So I started by installing FreeBSD-utilities in an empty base jail.
# Storage and required directories
ZFSPOOL=zroot
JAILSROOT=/jails
BASEJAIL=minimized-151
zfs create ${ZFSPOOL}${JAILSROOT}/base/${BASEJAIL}
mkdir -p ${JAILSROOT}/base/${BASEJAIL}/var/cache/pkg
mkdir -p ${JAILSROOT}/base/${BASEJAIL}/var/db/pkg/repos
# Mount the package cache and repositories in the basejail
mount_nullfs /var/cache/pkg ${JAILSROOT}/base/${BASEJAIL}/var/cache/pkg
mount_nullfs /var/db/pkg/repos ${JAILSROOT}/base/${BASEJAIL}/var/db/pkg/repos
# Install
pkg -r ${JAILSROOT}/base/set-base-jail-151 install -r FreeBSD-base FreeBSD-utilities
# 22 packages installed, including FreeBSD-runtime
After this it became a bit trial-and-error, and hunting to find out why things were failing.
As I'm no longer building packages but use the "latest" from FreeBSD, results may differ for you, e.g. curl can be built with Kerberos disabled.
If you find out what file is missing, you can use pkg which on the a set-base-jail jail to find the corresponding package.
Packages that were added, and (when available) notes on why.
FreeBSD-libexecinfo: First hurdle was backtrace.FreeBSD-rc: No services were starting in jails (doh!). This also pulls inFreeBSD-mtree.FreeBSD-acct:net-p2p/transmission-daemonrelies on/usr/sbin/utx, whichpkg whichwill tell you the source of.FreeBSD-kerberos-lib:ftp/curl(a dependency of just about anything) requires Kerberos.FreeBSD-syslogd: My jails run syslogd and forward via syslog to the host so the jail name is retained in logs.FreeBSD-caroot: All IRC networks I'm on are connected using TLS, so we need CA roots. This also pulls incertctlandopenssl.FreeBSD-libdwarf: Either Rspamd or ClamAV require libdwarf.
What did we win?
| base jail | packages | zfs used | uncompressed | files |
|---|---|---|---|---|
| ezjail-admin | - | 472M | 813M | 29028 |
| set-base-jail | 188 | 350M | 633M | 28185 |
| minimized | 32 | 45.5M | 98M | 4788 |
It's always been about reducing the attack surface, can't break what's not there, can't live-of-the-land using what's not there. Gained a bit of space in the process too!
Now the process of upgrading the jails can start...
