poudriere: build your own packages

freebsd 15 / published / guide #50 / ≈ 4 min read

poudriere is the FreeBSD tool for building your own clean copies of packages from the ports tree. The official pkg repo is generic — every package built with the project's default options. With poudriere you build the same packages with the options you want, on the kernel you run, all reproducibly, all in a jailed clean environment.

Who is this for? Anyone who needs a single port option changed, or runs more than one FreeBSD machine and wants a private package repository to share, or maintains a port and wants to test changes before submitting them.

Why bother — when pkg works fine

  • You need nginx with a non-default module (Lua, Brotli, …).
  • You want updates pinned to a quarterly branch but with one bleeding-edge package.
  • You run a fleet — building once, distributing to N machines beats every machine rebuilding from ports.
  • You want everything signed by your own key.

If none of those apply, stick with pkg install. poudriere is a power tool, not a daily one.

Concepts to know up front

  • Jail — a clean FreeBSD environment poudriere builds in. Each jail is one version of FreeBSD (e.g. 13-2, 14-3). Build outputs are tagged with the jail name.
  • Ports tree — the recipes (Makefiles + patches) under /usr/ports traditionally. poudriere keeps its own copy under /usr/local/poudriere/ports/.
  • Set — an optional sub-namespace, used to keep different option configurations apart (e.g. desktop vs server).
  • Build queue — the list of ports you ask to be built. poudriere figures out dependencies and builds them all in order.

Step 1: install

sudo pkg install poudriere

Edit the config:

sudo nano /usr/local/etc/poudriere.conf

The minimum to change (the file is heavily commented):

# where the build outputs live
ZPOOL=zroot               # if you're on ZFS — leave commented if UFS
FREEBSD_HOST=https://download.freebsd.org

# distfiles cache, kept across builds
DISTFILES_CACHE=/usr/ports/distfiles

# how many parallel builders
USE_TMPFS=all
PARALLEL_JOBS=4

On ZFS, poudriere uses datasets for cheap copy-on-write clones of build jails — much faster builds. On UFS it's slower but works.

Step 2: create a build jail

sudo poudriere jail -c -j 14-3 -v 14.3-RELEASE

That bootstraps a clean FreeBSD 14.3 base under poudriere's control. Takes a few minutes. List jails any time with:

poudriere jail -l

Step 3: pull a ports tree

sudo poudriere ports -c -p default

That clones the ports tree into /usr/local/poudriere/ports/default. Update it later with:

sudo poudriere ports -u -p default

Step 4: pick what to build

Make a build list — one port origin per line:

sudo nano /usr/local/etc/poudriere.d/mylist
www/nginx
shells/bash
editors/vim
ports-mgmt/pkg

The origin is category/name — the same as the directory under /usr/ports.

Step 5: configure options for a port

The point of using poudriere is custom options. Edit them per-port:

sudo poudriere options -p default -j 14-3 -f /usr/local/etc/poudriere.d/mylist

A menu pops up for every port whose options have changed since the last run. Pick what you want, save with OK. Your choices are persisted under /usr/local/etc/poudriere.d/options/ and re-used on every future build.

Step 6: build!

sudo poudriere bulk -p default -j 14-3 -f /usr/local/etc/poudriere.d/mylist

You'll see a progress dashboard: builds queued, building, finished, failed. Big builds (Rust, Firefox, …) take time even on fast hardware. Walk away. Come back.

When done, the built packages live under:

/usr/local/poudriere/data/packages/14-3-default/All/

Step 7: use the local repo from this machine

poudriere can serve packages over HTTP from any web server (nginx is the natural pick), or you can use them locally. For local use, point pkg at the directory:

sudo nano /usr/local/etc/pkg/repos/local.conf
local: {
    url: "file:///usr/local/poudriere/data/packages/14-3-default",
    enabled: yes,
    priority: 100
}
FreeBSD: { enabled: no }

(Disable the official repo so pkg doesn't fall back to it — otherwise pkg picks the higher-version one regardless of source.)

sudo pkg update -f
sudo pkg upgrade

pkg now reads from your local builds.

Step 8 (optional): serve to other machines

On the build machine, run an nginx (or anything) serving /usr/local/poudriere/data/packages/14-3-default/ over HTTP. On the client machines, the same local.conf but with url: "http://build.lan/packages/14-3-default".

Sign the repo for safety:

sudo openssl genrsa -out /etc/ssl/keys/poudriere.key 4096
sudo openssl rsa -in /etc/ssl/keys/poudriere.key -pubout -out /etc/ssl/keys/poudriere.cert

Add to poudriere.conf:

PKG_REPO_SIGNING_KEY=/etc/ssl/keys/poudriere.key

And on clients, set signature_type: pubkey and pubkey: /etc/ssl/keys/poudriere.cert.

Keeping it going

Routine maintenance, every now and then:

sudo poudriere ports -u -p default                                # update ports tree
sudo poudriere jail -u -j 14-3                                     # update build jail (after FreeBSD updates)
sudo poudriere bulk -p default -j 14-3 -f /usr/local/etc/poudriere.d/mylist     # rebuild
sudo poudriere logclean -p default -j 14-3                         # tidy old build logs
sudo poudriere pkgclean -p default -j 14-3 -f /usr/local/etc/poudriere.d/mylist # remove packages no longer in your list

Useful inspection

poudriere status                          # what's currently building, if anything
poudriere logclean -n 30                  # how much log space is being used
ls -lh /usr/local/poudriere/data/packages/14-3-default/All/

When a build fails

poudriere keeps full logs:

ls /usr/local/poudriere/data/logs/bulk/14-3-default/latest/logs/errors/

Each file is the build log of one failed port. Read it; the error is usually at the bottom.

The reality check

poudriere is a real time investment to set up — but for one specific class of person (someone running >2 FreeBSD machines, or wanting non-default port options) it pays for itself within weeks. For everyone else, pkg install is fine and you can skip this guide entirely.

← back to index