Hello everyone! After some time around playing with pkgbase, I've found a
way for making minimal OCI\Podman\Docker-like chroot environments where theres
only an app (could be many of them, though) and its dependencies inside a
chroot environment. No need for managing 500+MB bases or having custom minimal
builds of FreeBSD (although with some nullfs magic having a shared base
could actually sometimes be a better choice
).
I believe this is the first real full tutorial how to make this work, and
if someone has more experience and knowledge about stuff in this post -- I
would really welcome the feedback, that would be net positive for everyone.
The real requirements for you would be to have
enabled for use with pkg(8) and knowing how to mount and unmount filesystems.
As an example, i will create a chroot/jail environment
with editors/neovim.
In order for
two things are present in the target directory at the time of running pkg(8):
Install
Dont forget to mount devfs and
run
the new chroot.
We need to create the chroot directory, duh...
In order for pkg to work properly, I chose to copy the repository keys and
use
probably nullfs-mount the keys for that extra 4K of space savings.
Now we are free to utilize
cases (as with vim-tiny port)
is not needed (because vim(1)
just needs C libraries bundled with
automatically installed dependency). FreeBSD-runtime package will usually be
pulled automatically as a dependency and it contains
for fixing later problems. I'd recommend to explicitly install
'FreeBSD-runtime'.
If you are feeling extreme, you can delete all manpages from the
chroot environment that got pulled by packages (and yet we dont have
chroot will pull them back.
I'd write how much space it saved, but at the moment of editing this post, it
looks like
(it had some manpages for C libraries). Maybe it was all just a dream
.
After originally testing this stuff on FreeBSD 15-ALPHA3, I noticed that
on FreeBSD 14.3 you have to manually run
That's it! Now we need to unmount tmpfs stuff, effectively purging the
package manager metadata cache.
inprevious steps and use the pkg commands we used, but this time
replacing
.
We now can use this chroot directory with
(editors/neovim, in this case) really need devfs to be present.
Be warned that when you exit
unmount devfs mountsbecause of some dark jail magic logic.
way for making minimal OCI\Podman\Docker-like chroot environments where theres
only an app (could be many of them, though) and its dependencies inside a
chroot environment. No need for managing 500+MB bases or having custom minimal
builds of FreeBSD (although with some nullfs magic having a shared base
could actually sometimes be a better choice

I believe this is the first real full tutorial how to make this work, and
if someone has more experience and knowledge about stuff in this post -- I
would really welcome the feedback, that would be net positive for everyone.
The real requirements for you would be to have
FreeBSD-base
repositoryenabled for use with pkg(8) and knowing how to mount and unmount filesystems.
As an example, i will create a chroot/jail environment
with editors/neovim.
Short answer to our problems
In order for
pkg -r $directory
to work properly, you need to ensuretwo things are present in the target directory at the time of running pkg(8):
- /usr/share/keys/pkg/trusted/pkg.freebsd.org.2013102301 -- just copy/nullfs mount the thing
- /var/db/pkg/repos -- temporarily mounting as tmpfs recommended because this directory contains metadata cache
Install
FreeBSD-runtime
package and packages you need.Dont forget to mount devfs and
run
ldconfig -m /usr/local/lib
insidethe new chroot.
Full tutorial
We need to create the chroot directory, duh...
Code:
mkdir -p jail-nvim
In order for pkg to work properly, I chose to copy the repository keys and
use
tmpfs
for storing package manager metadata cache. You canprobably nullfs-mount the keys for that extra 4K of space savings.
Code:
# create the directory layout
mkdir -p jail-nvim/usr/share/keys/pkg/trusted
mkdir -p jail-nvim/var/db/pkg/repos
# copy the repository keys
cp -rn /usr/share/keys/pkg/trusted jail-nvim/usr/share/keys/pkg
# tmpfs mount the pkg metadata cache
mount -t tmpfs tmpfs jail-nvim/var/db/pkg/repos
Now we are free to utilize
-r
option with pkg. Note that in somecases (as with vim-tiny port)
FreeBSD-runtime
packageis not needed (because vim(1)
just needs C libraries bundled with
FreeBSD-clibs
package, anautomatically installed dependency). FreeBSD-runtime package will usually be
pulled automatically as a dependency and it contains
ldconfig
utilityfor fixing later problems. I'd recommend to explicitly install
'FreeBSD-runtime'.
Code:
pkg -r jail-nvim install FreeBSD-runtime neovim
pkg -r jail-nvim clean -a # we dont need cached packages and other stuff inside our little jail :)
If you are feeling extreme, you can delete all manpages from the
chroot environment that got pulled by packages (and yet we dont have
man
utility installed).Note that updating packages in ourchroot will pull them back.
I'd write how much space it saved, but at the moment of editing this post, it
looks like
FreeBSD-runtime
package no longer supplies manpages(it had some manpages for C libraries). Maybe it was all just a dream

Code:
rm -rf /usr/share/man # system manpages
rm -rf /usr/local/share/man # ports manpages
After originally testing this stuff on FreeBSD 15-ALPHA3, I noticed that
on FreeBSD 14.3 you have to manually run
ldconfig
inside the chroot.
Code:
chroot jail-nvim ldconfig -m /usr/local/lib
That's it! Now we need to unmount tmpfs stuff, effectively purging the
package manager metadata cache.
Code:
umount jail-nvim/var/db/pkg/repos
Maintenance and testing
To update packages inside this minimal chroot we need to mount tmpfs likeinprevious steps and use the pkg commands we used, but this time
replacing
install
to upgrade
. Don't forget to use pkg clean

We now can use this chroot directory with
path
option in jail
utility or jail.conf
. Keep in mind that most software(editors/neovim, in this case) really need devfs to be present.
Be warned that when you exit
neovim
, jail(8) utility will notunmount devfs mounts
Code:
doas jail -c \
path="$(realpath ./jail-nvim)" \
mount.devfs \
command=nvim
# now we umount it
umount $(realpath ./jail-nvim/dev)