Creating Custom Packages on FreeBSD

Hi folks, long time no see. Today we are going to cover how to build pkg(8) (pkgng) packages away from the FreeBSD ports tree. This is useful for external/private repositories (mostly bigger ones or generally fast-moving) or non-conventional ports like database content or even system updates. pkg(8) itself provides the essential pkg-create(8) tool, as well as pkg-query(8), which we will use to generate embedded dependencies.

Custom packages can be used in a wide variety of tasks, meaning you don’t have to ship conventional ports. One of my personal favourites is the automation of update tasks and system administration. In theory, a package upgrade could reconfigure your systems according to the latest security policies, so all you have to do on the remote machine is to create a cron job for pkg-upgrade(8) or trigger the installation manually using a single shell command.

First, a couple of files will have to be set up in this imaginary package:


rm -rf ${STAGEDIR}
mkdir -p ${STAGEDIR}

# careful here, this may clobber your system
echo "Resetting root shell"
pw usermod -n root -s /bin/csh

# careful here, this may clobber your system
echo "Registering root shell"
pw usermod -n root -s /bin/sh

There is +PRE_DEINSTALL, +POST_DEINSTALL, +PRE_INSTALL as well as +POST_INSTALL. Generally, you want to run post-install and pre-deinstall tasks as it makes sure your files are in place when you do the system manipulation, but that’s just a rule of thumb. Next, the master file +MANIFEST is written:

name: mypackage
version: "1.0_5"
origin: sysutils/mypackage
comment: "automates stuff"
desc: "automates tasks which can also be undone later"
prefix: /

This will get you going, but what if the package has dependencies that should be enforced? Maybe the package is simply a meta-package that holds all essential packages so you can design fast customised system setups? If the packages are installed on the build system, then pkg-query(8) may be used to generate these dependencies:

echo "deps: {" >> ${STAGEDIR}/+MANIFEST
pkg query "  %n: { version: \"%v\", origin: %o }" portlint >> ${STAGEDIR}/+MANIFEST
pkg query "  %n: { version: \"%v\", origin: %o }" poudriere >> ${STAGEDIR}/+MANIFEST
echo "}" >> ${STAGEDIR}/+MANIFEST

The benefit of pkg-query(8) is that it will format and output the correct information so one doesn’t have to triple-check for the correct info (trust me, I’ve been there). Now, shipping files inside the package requires another file, namely plist:

mkdir -p ${STAGEDIR}/usr/local/etc
echo "# hello world" > ${STAGEDIR}/usr/local/etc/my.conf
echo "/usr/local/etc/my.conf" > ${STAGEDIR}/plist

The last step uses pkg-create(8) to build a package:

pkg create -m ${STAGEDIR}/ -r ${STAGEDIR}/ -p ${STAGEDIR}/plist -o .

From here on out, you’ll be able to use the package archive file with pkg-add(8). In the next couple of posts, we’ll look deeper into pkg(8), e.g. how to create a custom package mirror, how to properly shield your build system using chroot(8), etc.

2 thoughts on “Creating Custom Packages on FreeBSD”

  1. Hi Oliver,

    thanks for looking into this and OPNsense. :) The script(s) come from the OPNsense tools.git. I was going to write more tutorials on the stuff that is there and how to develop in the environment.

    And, yeah, poudriere is a nice solution. I was looking at it once and it did all the things one could imagine, but in a way that is too closed-down in itself. It’s hard to explain. It would e.g. delete a custom git tree off of the build machine because it was “done building”. When you lose your ports tree and you don’t have access to the remote it’s kinda tricky….

    I want to look at it again soon and maybe see if it can be tweaked/configured in that regard. :)

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.