🏭 poudriere: Build Your Own Packages, Properly
Gloves made once, in a clean workshop, and worn on every machine you own. Build a custom package repository in throwaway jails and install from it with plain pkg
Why not just build on the host?
On the ports page we built nginx straight on the machine that would run it. It worked, and it is how most people start. It also has costs, and you saw several of them:
- The build tools stay behind. Compiling needed perl5, texinfo, autoconf and more: 15 packages and 103 MiB that had to be tidied up afterwards.
- A mixed-up tree can make a mess. When the ports tree and the package repository disagreed about pcre2, the build wandered off into twenty minutes of compiling its own tools.
- Everything happens on a live system. The build can use all the memory and CPU of a server that has a day job, and a half-finished build leaves half-installed things.
- Every machine has to repeat it. Ten servers, ten compiles.
poudriere (say "poo-dree-air") is the tool the FreeBSD project itself uses to build its official packages (from the documentation), and you can use it too. It builds your chosen ports inside throwaway jails from a clean starting point, and puts the results into a package repository: a plain folder that pkg understands. Then every machine you own installs those packages with pkg install, exactly as it would from the official repository, with no compiler involved.
In mittens-and-gloves terms: ports on the host are making gloves at the kitchen table. poudriere is the glove workshop: made once, to your pattern, and worn on every hand.
ports tree + your options + a clean build jail
|
poudriere bulk (builds each port in a fresh jail)
|
a package repository (a folder of .pkg files)
|
pkg install on every machine (plain mittens-style install)
Everything below was run for real on FreeBSD 15.0-RELEASE in a small virtual machine (2 processors, 2 GB of memory), with the ZFS layout from the other pages.
Step 1: install and configure
root@practice:~ # pkg install -y poudriere 2>&1 | grep -E "poudriere|Number of" poudriere: 3.4.8 [FreeBSD-ports] Number of packages to be installed: 3 [1/3] Fetching poudriere-3.4.8: .......... done [3/3] Installing poudriere-3.4.8... [3/3] Extracting poudriere-3.4.8: .......... done
poudriere is itself a package (about 1 MB, plus a list of release manifests). Its settings live in /usr/local/etc/poudriere.conf, and ours is short:
- ZPOOL=zroot and ZROOTFS=/poudriere: poudriere is built for ZFS. It keeps its jails, packages and logs in datasets under zroot/poudriere, which is what makes it fast to clone and clean up. (There is a no-ZFS mode, but ZFS is the way: from the documentation, not run here.)
- BASEFS=/usr/local/poudriere: where it all appears on disk.
- FREEBSD_HOST=https://download.FreeBSD.org: where to fetch FreeBSD from when it creates a jail. We will not need it this time.
- RESOLV_CONF=/etc/resolv.conf: the DNS settings the build jails use, since ports download their source.
- USE_TMPFS=no: the default is yes, which does builds in memory and is faster. Our machine has only 2 GB, so we told it to use the disk. On a real build server with plenty of memory, leave it on (from the documentation, not run here).
- DISTFILES_CACHE=/usr/ports/distfiles: source downloads are kept here and reused, so a second build does not download again.
Step 2: a build jail
poudriere builds in a jail that holds a clean copy of a FreeBSD release. The jail's FreeBSD version matters: packages are built for that release, so the jail should match (or be older than) the machines that will install them (from the documentation, not run here). Normally poudriere downloads the release for you. We have the installation disc, so we used its base.txz and avoided a download:
root@practice:~ # cp /mnt/usr/freebsd-dist/base.txz /var/tmp/ root@practice:~ # ls -lh /var/tmp/base.txz -rw-r--r-- 1 root wheel 157M Oct 6 11:57 /var/tmp/base.txz root@practice:~ # poudriere jail -c -j j15 -v 15.0-RELEASE -m tar=/var/tmp/base.txz [00:00:00] Creating j15 fs at /usr/local/poudriere/jails/j15... done [00:00:00] Installing 15.0-RELEASE amd64 from /var/tmp/base.txz ... done [00:00:09] Recording filesystem state for clean... done [00:00:09] Jail j15 15.0-RELEASE amd64 is ready to be used root@practice:~ # poudriere jail -l JAILNAME VERSION ARCH METHOD TIMESTAMP PATH j15 15.0-RELEASE amd64 tar 2026-10-06 11:57:38 /usr/local/poudriere/jails/j15
poudriere jail -c -j j15 -v 15.0-RELEASE -m tar=FILE created a jail named j15 from the archive, in 9 seconds. (Without -m tar= it fetches the release over the network: from the documentation, not run here.) poudriere jail -l lists the jails. It also recorded the clean state of the filesystem ("Recording filesystem state for clean"), so every build can start from it.
Step 3: a ports tree
root@practice:~ # poudriere ports -c -p default -m null -M /usr/ports [00:00:00] Imported ports tree "default" from /usr/ports root@practice:~ # poudriere ports -l PORTSTREE METHOD TIMESTAMP PATH default null 2026-10-06 11:57:47 /usr/ports root@practice:~ # zfs list -r -o name,used,mountpoint zroot/poudriere NAME USED MOUNTPOINT zroot/poudriere 375M /usr/local/poudriere zroot/poudriere/data 688K /usr/local/poudriere/data zroot/poudriere/data/.m 96K /usr/local/poudriere/data/.m zroot/poudriere/data/cache 96K /usr/local/poudriere/data/cache zroot/poudriere/data/images 96K /usr/local/poudriere/data/images zroot/poudriere/data/logs 96K /usr/local/poudriere/data/logs zroot/poudriere/data/packages 96K /usr/local/poudriere/data/packages zroot/poudriere/data/wrkdirs 96K /usr/local/poudriere/data/wrkdirs zroot/poudriere/jails 374M /usr/local/poudriere/jails zroot/poudriere/jails/j15 374M /usr/local/poudriere/jails/j15 zroot/poudriere/ports 96K /usr/local/poudriere/ports
poudriere keeps named ports trees. poudriere ports -c -p default -m null -M /usr/ports registered our existing /usr/ports as the tree called default (-m null means "use this directory, do not fetch anything"). Normally you let poudriere fetch and update its own tree with -m git and poudriere ports -u (from the documentation, not run here). The zfs list that follows shows the datasets poudriere made: the jail (374 MB), and places for the packages, logs and work areas.
A reminder from the ports page: our tree is from November 2025, so the packages we build are older than the official ones (you will see nginx 1.28.0 against the official 1.30.6). On a real machine, keep the tree fresh.
Step 4: your options and your package list
root@practice:~ # cat > /usr/local/etc/poudriere.d/make.conf <<'EOF' > www_nginx_UNSET=MAIL MAIL_SSL HTTP_DAV HTTP_FLV HTTP_MP4 HTTP_RANDOM_INDEX HTTPV3 WWW > www_nginx_SET=HTTP_DEGRADATION > EOF root@practice:~ # cat > /usr/local/etc/poudriere.d/pkglist <<'EOF' > www/nginx > sysutils/tree > EOF root@practice:~ # ls /usr/local/etc/poudriere.d hooks jails make.conf pkglist ports
- /usr/local/etc/poudriere.d/make.conf is the same kind of file as /etc/make.conf on the ports page: the options for each port. It holds our lean nginx choices: no WebDAV, FLV, MP4, mail, HTTP/3 or sample pages, and the degradation module on. We left out CPUTYPE?=native on purpose. Packages in a repository may be installed on different processors, and native code can fail to run on an older one. Build for a generic CPU unless every machine is identical.
- pkglist is a plain list of the ports we want: www/nginx and sysutils/tree. poudriere works out the dependencies by itself.
- In bigger setups you can have separate jails, ports trees and sets of options (-z SET), for example one repository for web servers and another for databases. And poudriere options -j j15 -p default www/nginx opens the familiar options dialog for a port and saves the answers where poudriere will find them (from the documentation, not run here).
Step 5: look before you build
root@practice:~ # poudriere bulk -n -j j15 -p default -f /usr/local/etc/poudriere.d/pkglist [00:00:00] [Dry Run] Creating the reference jail... done [00:00:01] [Dry Run] Mounting ports from: /usr/ports [00:00:01] [Dry Run] Appending to make.conf: /usr/local/etc/poudriere.d/make.conf [00:00:04] [Dry Run] Gathering ports metadata [00:00:04] [Dry Run] Calculating ports order and dependencies [j15-default] [2026-10-06_11h57m56s] [load_priorities] Queued: 23 Built: 0 Failed: 0 Skipped: 0 Ignored: 0 Fetched: 0 Tobuild: 23 Time: 00:00:01 [00:00:05] [Dry Run] Would build 23 packages using 2 builders [00:00:05] [Dry Run] Ports to build: converters/libiconv converters/p5-Text-Unidecode devel/autoconf devel/autoconf-switch devel/automake devel/gettext-runtime devel/gettext-tools devel/gmake devel/libtextstyle devel/libtool devel/m4 devel/p5-Locale-gettext devel/p5-Locale-libintl devel/pcre2 devel/pkgconf lang/perl5.42 misc/help2man ports-mgmt/pkg print/indexinfo print/texinfo sysutils/tree textproc/p5-Unicode-EastAsianWidth www/nginx
poudriere bulk -n is a dry run: it does everything except build. It sets up the jail, loads our make.conf, works out the dependencies and says what it would do: build 23 packages using 2 builders. We asked for two ports. The other 21 are everything they need, from perl5 and autoconf to pcre2. (The long Ports to build line is the real output: scroll sideways to read it.) Run a dry run first whenever you change the list or the options: it takes five seconds and shows you the size of the job.
Step 6: build
root@practice:~ # time poudriere bulk -j j15 -p default -f /usr/local/etc/poudriere.d/pkglist
[00:00:00] Creating the reference jail... done
[00:00:00] Mounting ports from: /usr/ports
[00:00:00] Appending to make.conf: /usr/local/etc/poudriere.d/make.conf
[00:00:03] Calculating ports order and dependencies
[00:00:04] Building 23 packages using up to 2 builders
[00:00:04] Hit CTRL+t at any time to see build progress and stats
[00:02:49] [01] [00:02:45] Finished ports-mgmt/pkg | pkg-2.4.2: Success
[00:02:55] [01] [00:00:06] Finished print/indexinfo | indexinfo-0.3.1_1: Success
[00:04:36] [01] [00:01:41] Finished devel/gettext-runtime | gettext-runtime-0.23.1: Success
[00:06:34] [01] [00:01:58] Finished devel/libtextstyle | libtextstyle-0.23.1: Success
[00:10:24] [02] [00:07:35] Finished lang/perl5.42 | perl5-5.42.0_1: Success
[00:12:04] [02] [00:01:40] Finished devel/gmake | gmake-4.4.1: Success
[00:12:11] [01] [00:05:37] Finished devel/gettext-tools | gettext-tools-0.23.1_1: Success
[00:12:15] [01] [00:00:04] Finished devel/p5-Locale-gettext | p5-Locale-gettext-1.07: Success
[00:12:18] [01] [00:00:03] Finished converters/p5-Text-Unidecode | p5-Text-Unidecode-1.30: Success
[00:12:20] [01] [00:00:02] Finished textproc/p5-Unicode-EastAsianWidth | p5-Unicode-EastAsianWidth-12.0: Success
[00:12:25] [01] [00:00:05] Finished misc/help2man | help2man-1.49.3_1: Success
[00:12:26] [01] [00:00:01] Finished devel/autoconf-switch | autoconf-switch-20220527: Success
[00:12:40] [01] [00:00:14] Finished devel/pkgconf | pkgconf-2.4.3,1: Success
[00:12:41] [02] [00:00:36] Finished converters/libiconv | libiconv-1.17_1: Success
[00:12:44] [01] [00:00:04] Finished sysutils/tree | tree-2.2.1: Success
[00:12:48] [02] [00:00:07] Finished devel/p5-Locale-libintl | p5-Locale-libintl-1.35: Success
[00:14:10] [01] [00:01:22] Finished print/texinfo | texinfo-7.1_12,1: Success
[00:15:28] [01] [00:01:18] Finished devel/m4 | m4-1.4.19_1,1: Success
[00:15:42] [01] [00:00:14] Finished devel/autoconf | autoconf-2.72: Success
[00:17:00] [02] [00:01:32] Finished devel/libtool | libtool-2.5.4_1: Success
[00:17:07] [01] [00:01:25] Finished devel/automake | automake-1.17: Success
[00:19:30] [01] [00:02:22] Finished devel/pcre2 | pcre2-10.46: Success
[00:21:05] [01] [00:01:35] Finished www/nginx | nginx-1.28.0_3,3: Success
[00:21:05] Creating pkg repository
[00:21:06] Committing packages to repository: /usr/local/poudriere/data/packages/j15-default/.real_1791289153 via .latest symlink
[00:21:06] Cleaning up
1266.12 real 1579.61 user 226.23 sys
(The output is shortened: setup lines and the repository progress bars are left out.) What you are looking at:
- poudriere starts a reference jail, mounts the ports tree, the package folder and the distfiles, and appends our make.conf. It will build as an ordinary user (nobody), not as root.
- It then runs up to two builders at the same time, each in its own fresh jail cloned from the clean state. Building ports-mgmt/pkg, Finished ... Success: one line per package. Dependencies come first: pkg itself, then perl5, gettext, autoconf... and www/nginx last, once everything it needs exists.
- perl5 took the longest (7 minutes 35 seconds). nginx itself took 1 minute 35 seconds. tree, with almost no dependencies, took 4 seconds.
- Every build starts from a clean system, so nothing from an earlier build can leak in, and a failure cannot damage the host.
- Finally poudriere creates the repository and commits it. All 23 built, with 0 failures, in 21 minutes (about 30 minutes of processor time in all, spread over 2 processors).
If a package fails, poudriere does not stop: it builds everything else it can, and lists the failures at the end, with a log file for each (from the documentation, not run here).
What you have after a build
root@practice:~ # ls /usr/local/poudriere/data/packages/j15-default .buildname .latest All data.pkg logs meta.conf packagesite.tzst .jailversion .real_1791289153 Latest data.tzst meta packagesite.pkg root@practice:~ # ls /usr/local/poudriere/data/packages/j15-default/All autoconf-2.72.pkg gmake-4.4.1.pkg libtool-2.5.4_1.pkg p5-Text-Unidecode-1.30.pkg pkgconf-2.4.3,1.pkg autoconf-switch-20220527.pkg help2man-1.49.3_1.pkg m4-1.4.19_1,1.pkg p5-Unicode-EastAsianWidth-12.0.pkg texinfo-7.1_12,1.pkg automake-1.17.pkg indexinfo-0.3.1_1.pkg nginx-1.28.0_3,3.pkg pcre2-10.46.pkg tree-2.2.1.pkg gettext-runtime-0.23.1.pkg libiconv-1.17_1.pkg p5-Locale-gettext-1.07.pkg perl5-5.42.0_1.pkg gettext-tools-0.23.1_1.pkg libtextstyle-0.23.1.pkg p5-Locale-libintl-1.35.pkg pkg-2.4.2.pkg root@practice:~ # du -sh /usr/local/poudriere/data/packages/j15-default 42M /usr/local/poudriere/data/packages/j15-default
This folder is a package repository: All/ holds the 23 .pkg files, and the other files (packagesite.pkg, meta.conf, data.pkg) are the catalogue that pkg reads. 42 MB for everything. There is nothing else to install on a server: any web server (or even a file path) can serve it.
And now the part that makes poudriere pleasant:
root@practice:~ # time poudriere bulk -j j15 -p default -f /usr/local/etc/poudriere.d/pkglist 2>&1 | tail -9
[00:00:04] Committing packages to repository: /usr/local/poudriere/data/packages/j15-default/.real_1791289191 via .latest symlink
[00:00:04] Removing old packages
[j15-default] [2026-10-06_12h19m46s] [committing] Queued: 0 Built: 0 Failed: 0 Skipped: 0 Ignored: 0 Fetched: 0 Tobuild: 0 Time: 00:00:02
[00:00:04] Logs: /usr/local/poudriere/data/logs/bulk/j15-default/2026-10-06_12h19m46s
[00:00:05] Cleaning up
j15-default: removed
j15-default-n: removed
[00:00:05] Unmounting file systems
5.05 real 3.76 user 1.72 sys
Run the same build again and it takes 5 seconds: Queued: 0 Built: 0. poudriere noticed that every package is already up to date, and built nothing. When you update the ports tree or change an option, it rebuilds only the packages that changed, and the ones that depend on them (the second part is from the documentation, not run here). That is why a regular poudriere build server is quick after the first long run.
Using the repository
A machine learns about a repository from a small file in /usr/local/etc/pkg/repos/. First, a small trap:
root@practice:~ # cat > /usr/local/etc/pkg/repos/poudriere.conf <<'EOF'
> poudriere: {
> url: "file:///usr/local/poudriere/data/packages/j15-default",
> signature_type: "none",
> enabled: yes
> }
> FreeBSD-ports: { enabled: no }
> FreeBSD-ports-kmods: { enabled: no }
> EOF
-su: cannot create /usr/local/etc/pkg/repos/poudriere.conf: No such file or directory
The folder /usr/local/etc/pkg/repos does not exist on a new system, and the shell refused to create the file: No such file or directory. We had not noticed it at first, and the next steps quietly carried on without our repository (you will see where, in a moment). Make the folder first:
root@practice:~ # ls /usr/local/etc/pkg/repos
ls: /usr/local/etc/pkg/repos: No such file or directory
root@practice:~ # mkdir -p /usr/local/etc/pkg/repos
root@practice:~ # cat > /usr/local/etc/pkg/repos/poudriere.conf <<'EOF'
> poudriere: {
> url: "file:///usr/local/poudriere/data/packages/j15-default",
> signature_type: "none",
> enabled: yes
> }
> FreeBSD-ports: { enabled: no }
> FreeBSD-ports-kmods: { enabled: no }
> EOF
root@practice:~ # pkg update -f 2>&1 | tail -4
Fetching data: .. done
Processing entries: ... done
poudriere repository update completed. 23 packages processed.
All repositories are up to date.
root@practice:~ # pkg -vv | sed -n '/^Repositories:/,$p'
[... the two official repositories are shown with "enabled : no" ...]
poudriere: {
url : "file:///usr/local/poudriere/data/packages/j15-default",
enabled : yes,
priority : 0
}
root@practice:~ # pkg rquery '%n-%v' nginx
nginx-1.28.0_3,3
- The file defines a repository called poudriere with a file:// address (a folder on this same machine, which is fine for a test). signature_type: "none" means "do not check signatures": acceptable on one machine, but a repository for other machines should be signed (poudriere can sign it with a key you create, and pkg checks it: from the documentation, not run here).
- Two more lines switch off the official repositories, so this machine installs only from our workshop. (You can also leave them on, and pkg will pick between them: the best choice depends on whether you want your build to win.)
- pkg update -f reads the new catalogue: poudriere repository update completed. 23 packages processed. And pkg -vv lists the repositories it knows: the official ones are enabled : no, ours is yes.
- pkg rquery '%n-%v' nginx asks the repository what version it has: nginx-1.28.0_3,3. That is our build, not the official 1.30.6.
Installing it on another system (a jail)
Our jail from the jails page plays the part of "another machine". It has the official nginx 1.30.6, and we want it to have the poudriere build instead. Our first attempt used the -j option, the way we did on earlier pages:
root@practice:~ # pkg -j web install -y nginx 2>&1 | grep -E "INSTALLED|nginx|pcre2|Number|REINSTALLED|UPGRADED|DOWNGRADED" New packages to be INSTALLED: nginx: 1.30.6,3 [FreeBSD-ports] Number of packages to be installed: 1 [web.example.org] [1/1] Installing nginx-1.30.6,3...
It installed the official 1.30.6 again, from [FreeBSD-ports]. Why? pkg -j web runs pkg inside the jail and uses the jail's own repository settings (which still point at the official repository). Our new file is on the host. The option that uses the host's configuration on a folder is -r (root directory):
root@practice:~ # service -j web nginx stop Stopping nginx. Waiting for PIDS: 9616. root@practice:~ # pkg -r /jails/web delete -y nginx 2>&1 | tail -3 The operation will free 2 MiB. [1/1] Deinstalling nginx-1.30.6,3... [1/1] Deleting files for nginx-1.30.6,3: .......... done root@practice:~ # pkg -r /jails/web install -y nginx 2>&1 | grep -E "INSTALLED|nginx|pcre2|Number|REINSTALLED|UPGRADED|DOWNGRADED|REMOVED" New packages to be INSTALLED: nginx: 1.28.0_3,3 [poudriere] Number of packages to be installed: 1 [1/1] Installing nginx-1.28.0_3,3... [1/1] Extracting nginx-1.28.0_3,3: .......... done Message from nginx-1.28.0_3,3: https://www.nginx.com/blog/dynamic-modules-nginx-1-9-11/ and http://nginx.org/en/docs/ngx_core_module.html#load_module for /usr/local/libexec/nginx. root@practice:~ # service -j web nginx start 2>&1 | tail -2 nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful Starting nginx. root@practice:~ # jexec web /usr/local/sbin/nginx -V 2>&1 | tr ' ' '\n' | grep -E 'dav|flv|mp4|degradation|v3|mail_ssl' --with-http_degradation_module root@practice:~ # pkg -r /jails/web info nginx | grep -E 'Version|HTTP_DAV|HTTP_DEGRADATION|^WWW ' Version : 1.28.0_3,3 WWW : https://nginx.com/ HTTP_DAV : off HTTP_DAV_EXT : off HTTP_DEGRADATION: on root@practice:~ # fetch -qo - http://10.0.2.50/ | head -6 fetch: http://10.0.2.50/: Not Found root@practice:~ # ls -l /jails/web/usr/local/sbin/nginx -r-xr-xr-x 1 root wheel 1056096 Oct 6 12:19 /jails/web/usr/local/sbin/nginx
Now the plan works: nginx: 1.28.0_3,3 [poudriere], and the proof is in the checks:
- nginx -V inside the jail lists only --with-http_degradation_module of the modules we looked for: none of dav, flv, mp4, http_v3 or mail_ssl. The official build had all of them.
- pkg info shows the options this package was built with: HTTP_DAV: off, HTTP_DEGRADATION: on.
- fetch gets Not Found: exactly right, because we switched off the WWW option, which installs the sample "Welcome to nginx" page. There is no page to serve.
- The binary is 1,056,096 bytes, the lean size, against 1,296,512 for the official package.
Notice what did not happen on this machine: no compiler ran, and no build tools were installed. The jail installed a finished package. That is the whole idea: build once in the workshop, install everywhere with plain pkg.
For real machines, put the repository on a web server instead of a folder, and point the other machines at it:
poudriere: {
url: "https://pkg.example.org/j15-default",
signature_type: "pubkey",
pubkey: "/usr/local/etc/ssl/poudriere.pub",
enabled: yes
}
(An example, from the documentation, not run here: the address and the key are made up.)
Tidy up and keep going
root@practice:~ # poudriere pkgclean -n -j j15 -p default -f /usr/local/etc/poudriere.d/pkglist [00:00:00] Warning: Will delete anything not listed. To delete listed use -C. [00:00:04] Looking for unneeded packages [00:00:04] No stale packages to cleanup root@practice:~ # zfs list -o name,used zroot/poudriere zroot/poudriere/jails/j15 zroot/poudriere/data/packages NAME USED zroot/poudriere 420M zroot/poudriere/data/packages 42.0M zroot/poudriere/jails/j15 374M
- poudriere pkgclean -n (a dry run again) looks for packages in the repository that no longer match your list or your tree, and would remove them. Here: No stale packages to cleanup. Without -n, it deletes them.
- The zfs list shows the whole workshop costs 420 MB: the jail (374 MB) and the packages (42 MB). The build work areas are cleaned up after each build.
The routine from here is short: update the ports tree, run poudriere bulk (it rebuilds only what changed), and on each machine run pkg update and pkg upgrade.
Things to try next
All of these are from the documentation, not run here:
- poudriere testport: build a single port with its tests in a clean jail and report problems. This is how people check a port before they submit it.
- Different sets and trees: separate repositories for different jobs, or for the quarterly and latest branches.
- -b latest (or quarterly): use the official packages to satisfy build dependencies instead of compiling them yourself. It would have spared us most of the dependency builds, but it only works well if your ports tree matches the official branch. Ours, of course, did not.
- poudriere image: build bootable disk images with your packages already inside.
- A web page of results: poudriere writes an HTML report of every build to its logs folder, which you can serve with a web server.
- A separate build machine: build on one strong machine (or a virtual machine), and let your servers only install.
⚠️ Do not skip the signing for machines you do not fully control. An unsigned repository over HTTP means anybody in between could hand your servers anything. Sign the repository, check the signature on every machine, and serve it over HTTPS.
They were built in a jail<br>
and came out clean.
🧪 Try it yourself
In a FreeBSD virtual machine with ZFS (and a ports tree in /usr/ports):
1. Install poudriere and create a poudriere.conf with your pool. 2. Create a build jail with poudriere jail -c (from a release download or a base.txz). 3. Register your ports tree with poudriere ports -c -p default -m null -M /usr/ports. 4. Write a pkglist with one small port, such as sysutils/tree, and run a dry run with poudriere bulk -n. 5. Run the real build. How long did it take? What did it build? 6. Run it again. What does it say? 7. Add a repos/poudriere.conf to /usr/local/etc/pkg/repos (make the folder first) and run pkg update -f. 8. Install the package from your repository and check where it came from.
The cheat sheet
- Config: /usr/local/etc/poudriere.conf (ZPOOL, ZROOTFS, BASEFS, USE_TMPFS, DISTFILES_CACHE). Options in /usr/local/etc/poudriere.d/make.conf. List ports in a file, one per line (category/port).
- Jail: poudriere jail -c -j NAME -v 15.0-RELEASE [-m tar=base.txz], poudriere jail -l.
- Ports tree: poudriere ports -c -p default -m git (or -m null -M /usr/ports), poudriere ports -u, poudriere ports -l.
- Build: poudriere bulk -n -j NAME -p default -f LIST (dry run), then without -n. poudriere status, poudriere pkgclean -n.
- Repository: /usr/local/poudriere/data/packages/NAME-default/, config in /usr/local/etc/pkg/repos/NAME.conf (make the folder first), pkg update -f, pkg rquery '%n-%v' PKG.
- Install into a jail's folder with the host's config: pkg -r /jails/NAME install PKG (pkg -j NAME uses the jail's own config).
- Safety: sign the repository, serve it over HTTPS, and match the jail's release to your machines.
← bhyve: Virtual Machines Built into FreeBSD · ↑ FreeBSD Corner · vm-bhyve: Virtual Machines with Short Commands →