π§€ The Ports Tree: Building FreeBSD Software from Source
Mittens or gloves? Pre-built packages fit most hands. The ports tree lets you cut software to fit yours: your options, your modules, your CPU
Mittens and gloves
Here is the easiest way to remember the choice FreeBSD gives you:
- Packages (pkg) are mittens. Ready-made, one size fits nearly everyone, and you can put them on in seconds. They keep almost every hand warm, and most of the time that is all you need.
- The ports tree is gloves. The software is made to fit your hand: you choose which features go in, which are left out, and what the compiler optimises for. It takes longer to make them, and you have to look after them, but they fit exactly.
Both give you the same program in the end, installed in the same place and tracked by the same pkg database. The difference is who compiled it, and with which settings. Everything on this page was run for real on FreeBSD 15.0-RELEASE, which has the compiler (clang 19) built in. The upgrade page shows the mitten side.
What the ports tree is
The ports tree lives in /usr/ports. It is not software. It is a collection of recipes: one small folder per program, saying where to download the source, which patches to apply, which options exist, what the program needs and how to compile and install it. Our copy came off the installation disc, and unpacking it into the ZFS dataset that FreeBSD had already prepared for it took half a minute:
root@practice:~ # time tar -xf /mnt/usr/freebsd-dist/ports.txz -C /
29.53 real 8.26 user 19.59 sys
root@practice:~ # ls /usr/ports | head -12
.arcconfig
.gitignore
.hooks
.mailmap
CHANGES
CONTRIBUTING.md
COPYRIGHT
GIDs
Keywords
MOVED
Makefile
Mk
root@practice:~ # ls -d /usr/ports/*/ | wc -l
66
root@practice:~ # ls /usr/ports/sysutils | wc -l
1643
root@practice:~ # du -sh /usr/ports
932M /usr/ports
root@practice:~ # zfs list zroot/usr/ports
NAME USED AVAIL REFER MOUNTPOINT
zroot/usr/ports 902M 11.3G 902M /usr/ports
time shows 30 seconds, and ports.txz was a 52 MB archive (from its date on the disc, a snapshot of the tree from the end of November 2025). The tree has 66 category folders, such as sysutils, www and devel, and sysutils alone holds 1643 ports. Unpacked it is 932 MB. On a normal machine you fetch and update the tree with git (git clone to get it, and git pull to update: from the documentation, not run here), and the installer's snapshot is just the quick start.
Anatomy of a port
root@practice:~ # (cd /usr/ports/sysutils/tree && ls)
Makefile distinfo pkg-descr
root@practice:~ # (cd /usr/ports/sysutils/tree && cat Makefile)
PORTNAME= tree
DISTVERSION= 2.2.1
CATEGORIES= sysutils
MASTER_SITES= https://oldmanprogrammer.net/tar/tree/ \
https://fossies.org/linux/misc/
MAINTAINER= gb@unistra.fr
COMMENT= Display a tree-view of directories with optional color or HTML output
WWW= https://oldmanprogrammer.net/source.php?dir=projects/tree
LICENSE= GPLv2+
LICENSE_FILE= ${WRKSRC}/LICENSE
USES= gmake tar:tgz
MAKE_ARGS= CC="${CC}" CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}"
PLIST_FILES= bin/tree \
share/man/man1/tree.1.gz
PORTDOCS= CHANGES README TODO
OPTIONS_DEFINE= DOCS
do-install:
${INSTALL_PROGRAM} ${WRKSRC}/tree ${STAGEDIR}${PREFIX}/bin
${INSTALL_MAN} ${WRKSRC}/doc/*.1 ${STAGEDIR}${PREFIX}/share/man/man1
do-install-DOCS-on:
@${MKDIR} ${STAGEDIR}${DOCSDIR}
${INSTALL_DATA} ${PORTDOCS:S,^,${WRKSRC}/,} ${STAGEDIR}${DOCSDIR}
.include <bsd.port.mk>
root@practice:~ # (cd /usr/ports/sysutils/tree && make -V PORTVERSION -V PKGNAME -V MASTER_SITES)
2.2.1
tree-2.2.1
https://oldmanprogrammer.net/tar/tree/ https://fossies.org/linux/misc/
root@practice:~ # (cd /usr/ports/sysutils/tree && make pretty-print-build-depends-list)
pretty-print-build-depends-list requires an INDEX file (INDEX-15). Please run make index or make fetchindex.
A port is a few files. tree is a very small one, and its Makefile is the whole recipe, in a dozen lines:
- PORTNAME and DISTVERSION: the name and the version.
- MASTER_SITES: where to download the source code, in this case oldmanprogrammer.net.
- USES=gmake tar:tgz: what the build needs (gmake) and how the download is packed. Ports have a shared library of these USES helpers, which is why the files are so short.
- OPTIONS_DEFINE=DOCS: the choices you can make (just one here).
- do-install: how to install the program.
- distinfo holds the checksum of the source, so a corrupted or tampered download is refused. pkg-descr is the description.
make -V NAME asks the recipe for any of its variables (make -V PORTVERSION gave 2.2.1). The last command printed an error that teaches something: make pretty-print-build-depends-list needs an INDEX file, which you get with make fetchindex (from the documentation, not run here). Use make build-depends-list instead, as you will see next.
The options dialog
Many ports have options. Run make config in the port's folder and you get a small full-screen dialog:
βββββββββββββββββββββ€tree-2.2.1ββββββββββββββββββββββ
β 'F1' for Ports Collection help. β
β βββββββββββββββββββββββββββββββββββββββββββββββββ β
β βnew [X] DOCS Build and/or install documentationβ β
β βββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β [ OK ] [Cancel] β
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
This is the options dialog for tree: one option, DOCS (build and install the documentation), shown as [X] meaning on. Move with the arrow keys, toggle with the space bar, press Enter on OK. (The word new in front shows that the option had never been set before.) Your choice is saved to a file:
root@practice:/usr/ports/sysutils/tree # ls /var/db/ports/sysutils_tree && cat /var/db/ports/sysutils_tree/options options # This file is auto-generated by 'make config'. # Options for tree-2.2.1 _OPTIONS_READ=tree-2.2.1 _FILE_COMPLETE_OPTIONS_LIST=DOCS OPTIONS_FILE_SET+=DOCS
Next time the port is built, no questions are asked. The file lives in /var/db/ports/ under the port's name.
The classic gotcha: if the options have not been saved yet, make install opens this dialog by itself, and sits there waiting for a human, for every port that needs it, including the dependencies. Our first attempt at building tree seemed to hang for ten minutes: it was waiting at this dialog, and after we answered it, again for the dependency gmake. There are two cures: make config-recursive asks every question for the port and its dependencies up front, and BATCH=yes says "do not ask, accept the defaults", which is what scripts use.
Your first build
root@practice:~ # (cd /usr/ports/sysutils/tree && make build-depends-list)
/usr/ports/ports-mgmt/pkg
/usr/ports/devel/gmake
root@practice:~ # (cd /usr/ports/sysutils/tree && make showconfig)
===> The following configuration options are available for tree-2.2.1:
DOCS=on: Build and/or install documentation
===> Use 'make config' to modify these settings
root@practice:~ # (cd /usr/ports/sysutils/tree && env ASSUME_ALWAYS_YES=yes make install-missing-packages)
Updating FreeBSD-ports repository catalogue...
[... package catalogue lines left out ...]
New packages to be INSTALLED:
gettext-runtime: 1.0_1 [FreeBSD-ports]
gmake: 4.4.1 [FreeBSD-ports]
indexinfo: 0.3.1_1 [FreeBSD-ports]
Number of packages to be installed: 3
The process will require 4 MiB more space.
789 KiB to be downloaded.
Checking integrity... done (0 conflicting)
[1/3] Installing indexinfo-0.3.1_1...
[2/3] Installing gettext-runtime-1.0_1...
[3/3] Installing gmake-4.4.1...
make build-depends-list shows what tree needs to be built: pkg and gmake. Then a very useful trick: make install-missing-packages installs the missing dependencies as ready-made packages (mittens) rather than compiling them (gloves). That is how people mix the two styles: compile the program you care about, and take the supporting parts as packages. (We set ASSUME_ALWAYS_YES=yes so that pkg does not stop to ask.) Now the build:
root@practice:~ # (cd /usr/ports/sysutils/tree && time make install clean BATCH=yes)
===> tree-2.2.1 depends on package: gmake>=4.4.1 - found
===> Configuring for tree-2.2.1
===> Building for tree-2.2.1
cc -O2 -pipe -fstack-protector-strong -fno-strict-aliasing -DLARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c -o tree.o tr ...
cc -O2 -pipe -fstack-protector-strong -fno-strict-aliasing -DLARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c -o list.o li ...
cc -O2 -pipe -fstack-protector-strong -fno-strict-aliasing -DLARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c -o hash.o ha ...
cc -O2 -pipe -fstack-protector-strong -fno-strict-aliasing -DLARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c -o color.o c ...
cc -O2 -pipe -fstack-protector-strong -fno-strict-aliasing -DLARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c -o file.o fi ...
cc -O2 -pipe -fstack-protector-strong -fno-strict-aliasing -DLARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c -o filter.o ...
cc -O2 -pipe -fstack-protector-strong -fno-strict-aliasing -DLARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c -o info.o in ...
cc -O2 -pipe -fstack-protector-strong -fno-strict-aliasing -DLARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c -o unix.o un ...
cc -O2 -pipe -fstack-protector-strong -fno-strict-aliasing -DLARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c -o xml.o xml ...
cc -O2 -pipe -fstack-protector-strong -fno-strict-aliasing -DLARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c -o json.o js ...
cc -O2 -pipe -fstack-protector-strong -fno-strict-aliasing -DLARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c -o html.o ht ...
cc -O2 -pipe -fstack-protector-strong -fno-strict-aliasing -DLARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c -o strverscm ...
cc -o tree tree.o list.o hash.o color.o file.o filter.o info.o unix.o xml.o json.o html.o strverscmp.o
===> Staging for tree-2.2.1
===> Generating temporary packing list
install -s -m 555 /usr/ports/sysutils/tree/work/tree-2.2.1/tree /usr/ports/sysutils/tree/work/stage/usr/local/bin
install -m 444 /usr/ports/sysutils/tree/work/tree-2.2.1/doc/*.1 /usr/ports/sysutils/tree/work/stage/usr/local/share/m ...
install -m 0644 /usr/ports/sysutils/tree/work/tree-2.2.1/CHANGES /usr/ports/sysutils/tree/work/tree-2.2.1/README /usr ...
====> Compressing man pages (compress-man)
===> Installing for tree-2.2.1
===> Checking if tree is already installed
===> Registering installation for tree-2.2.1
Installing tree-2.2.1...
===> Cleaning for tree-2.2.1
6.36 real 6.99 user 1.15 sys
(Long lines are shortened.) The whole life of a port is in this output: it checks the dependencies, fetches the source, checks the checksum, configures, builds (the cc lines are the compiler at work, with the standard flags -O2 -pipe -fstack-protector-strong), stages the files into a temporary folder, installs them, registers the package and cleans up the work folder. For this tiny program it took 6.4 seconds. And afterwards:
root@practice:~ # which tree /usr/local/bin/tree root@practice:~ # tree --version tree v2.2.1 Β© 1996 - 2024 by Steve Baker, Thomas Moore, Francesc Rocher, Florian Sesser, Kyosuke Tokoro root@practice:~ # pkg info tree tree-2.2.1 Name : tree Version : 2.2.1 Installed on : Tue Oct 6 00:58:36 2026 UTC Origin : sysutils/tree Architecture : FreeBSD:15:amd64 Prefix : /usr/local Categories : sysutils Licenses : GPLv2+ Maintainer : gb@unistra.fr WWW : https://oldmanprogrammer.net/source.php?dir=projects/tree Comment : Display a tree-view of directories with optional color or HTML output Options : DOCS : on Shared Libs required: libc.so.7 Annotations : FreeBSD_version: 1500068 Flat size : 134KiB Description : Tree is a simple utility that displays a tree-view of directories and files. It has options to use ANSI color escape-sequences or ASCII graphic characters, and can also output in HTML format. root@practice:~ # pkg which /usr/local/bin/tree /usr/local/bin/tree was installed by package tree-2.2.1 root@practice:~ # ls /usr/ports/sysutils/tree Makefile distinfo pkg-descr
The result is an ordinary package. pkg info tree lists it with its Origin, sysutils/tree, and the options it was built with: DOCS: on. pkg which agrees that the file belongs to it. Nothing tells you whether it was compiled by you or downloaded: that is the point.
Gloves: building nginx to fit
Now a real fitting. www/nginx has about 90 options, including dozens of modules. The package from the repository has most of the common ones on. We want a lean web server: no WebDAV, FLV or MP4 streaming, no mail proxy, no HTTP/3, no sample pages, and the little degradation module switched on, with the compiler told to optimise for this machine's processor. All of that goes in /etc/make.conf, the file the build reads for your own settings:
root@practice:~ # echo 'CPUTYPE?=native' >> /etc/make.conf
root@practice:~ # echo 'www_nginx_UNSET=MAIL MAIL_SSL HTTP_DAV HTTP_FLV HTTP_MP4 HTTP_RANDOM_INDEX HTTPV3 WWW' >> /etc/make.conf
root@practice:~ # echo 'www_nginx_SET=HTTP_DEGRADATION' >> /etc/make.conf
root@practice:~ # cat /etc/make.conf
CPUTYPE?=native
www_nginx_UNSET=MAIL MAIL_SSL HTTP_DAV HTTP_FLV HTTP_MP4 HTTP_RANDOM_INDEX HTTPV3 WWW
www_nginx_SET=HTTP_DEGRADATION
root@practice:~ # (cd /usr/ports/www/nginx && make showconfig | grep -E 'HTTP_DAV|HTTP_FLV|HTTP_MP4|MAIL=|HTTPV3=|HTTP_DEGRADATION|WWW=|HTTP_REALIP')
WWW=off: Enable html sample files
HTTP_DAV=off: Enable http_webdav module
HTTP_DEGRADATION=on: Enable http_degradation module
HTTP_FLV=off: Enable http_flv module
HTTP_MP4=off: Enable http_mp4 module
HTTP_REALIP=on: Enable http_realip module
HTTPV3=off: Enable HTTP/3 protocol support
MAIL=off: Enable IMAP4/POP3/SMTP proxy module
HTTP_DAV_EXT=off: 3rd party webdav_ext module
- CPUTYPE?=native tells the compiler to use the features of this exact CPU.
- www_nginx_UNSET=... and www_nginx_SET=... switch options off and on for the port www/nginx (the pattern is category_portname_SET).
- make showconfig confirms what the build will use: HTTP_DAV=off, HTTP_DEGRADATION=on, and so on. Nobody opened a dialog.
A lesson in fitting the tree to the repository
When we started the build, it ran for more than twenty minutes, compiling perl5, texinfo and a chain of other tools, before we stopped it. Why?
root@practice:~ # (cd /usr/ports/devel/pcre2 && make -V PKGNAME) pcre2-10.46 root@practice:~ # pkg rquery '%n-%v' pcre2 pcre2-10.49 root@practice:~ # (cd /usr/ports/www/nginx && make -V PKGNAME) nginx-1.28.0_3,3 root@practice:~ # pkg rquery '%n-%v' nginx nginx-1.30.6,3 root@practice:~ # (cd /usr/ports/devel/pcre2 && make -V BUILD_DEPENDS) pkgconf>=1.3.0_1:devel/pkgconf autoconf>=2.72:devel/autoconf automake>=1.17:devel/automake libtoolize:devel/libtool
The ports tree and the package repository are two different snapshots in time. Our tree (from the end of November 2025) has pcre2-10.46, and the package repository today has pcre2-10.49. The nginx port wants pcre2, install-missing-packages looks for the version the tree asks for, and the repository did not have it. As far as we can tell, the build then set out to compile pcre2 itself. Its recipe (the last line above) needs autoconf, automake and libtool to build, and those pull in perl5 and more. The fix is to give it a version that satisfies the dependency: we installed pcre2 from the repository, and the build saw it was there.
root@practice:~ # (cd /usr/ports/www/nginx && time make install clean BATCH=yes)
===> License BSD2CLAUSE accepted by the user
===> nginx-1.28.0_3,3 depends on file: /usr/local/sbin/pkg - found
===> Fetching all distfiles required by nginx-1.28.0_3,3 for building
===> Extracting for nginx-1.28.0_3,3
=> SHA256 Checksum OK for nginx-1.28.0.tar.gz.
===> Patching for nginx-1.28.0_3,3
===> Applying FreeBSD patches for nginx-1.28.0_3,3 from /usr/ports/www/nginx/files
===> nginx-1.28.0_3,3 depends on shared library: libpcre2-8.so - found (/usr/local/lib/libpcre2-8.so)
===> Configuring for nginx-1.28.0_3,3
[... about 130 lines of configure checks left out ...]
===> Building for nginx-1.28.0_3,3
--- build ---
/usr/bin/make -f objs/Makefile
--- objs/src/core/nginx.o ---
--- objs/src/core/ngx_log.o ---
cc -c -O2 -pipe -march=native -fstack-protector-strong -fno-strict-aliasing -I /usr/local/include -I src/core -I sr ...
[... the other compile lines are left out ...]
===> Staging for nginx-1.28.0_3,3
===> Generating temporary packing list
===> Creating groups
===> Staging rc.d startup script(s)
===> Installing for nginx-1.28.0_3,3
===> Checking if nginx is already installed
===> Registering installation for nginx-1.28.0_3,3
Installing nginx-1.28.0_3,3...
===> Creating groups
===> Cleaning for nginx-1.28.0_3,3
93.44 real 139.84 user 16.34 sys
(The middle of the log is left out.) Now the build finds libpcre2-8.so, configures with the options we chose, and compiles with -march=native in every compile line: the glove being cut to fit. 93 seconds for the whole web server (about 140 seconds of processor time on 2 cores). The lesson: keep your ports tree and your package repository in step (update the tree with git, from the same branch the packages come from: from the documentation, not run here), or you will end up compiling things you did not mean to.
Mittens against gloves, side by side
On this machine, the host now runs nginx built from ports (gloves) and the jail from the jails page still runs the nginx package (mittens). That makes an honest comparison:
root@practice:~ # /usr/local/sbin/nginx -v nginx version: nginx/1.28.0 root@practice:~ # jexec web /usr/local/sbin/nginx -v nginx version: nginx/1.30.6 root@practice:~ # /usr/local/sbin/nginx -V 2>&1 | tr ' ' '\n' | grep -E 'dav|flv|mp4|degradation|v3|mail|march' --with-http_degradation_module --without-mail_imap_module --without-mail_pop3_module --without-mail_smtp_module root@practice:~ # jexec web /usr/local/sbin/nginx -V 2>&1 | tr ' ' '\n' | grep -E 'dav|flv|mp4|degradation|v3|mail|march' --with-http_v3_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --without-mail_imap_module --without-mail_pop3_module --without-mail_smtp_module --with-mail_ssl_module --with-mail=dynamic root@practice:~ # ls -l /usr/local/sbin/nginx /jails/web/usr/local/sbin/nginx -r-xr-xr-x 1 root wheel 1296512 Oct 1 05:54 /jails/web/usr/local/sbin/nginx -r-xr-xr-x 1 root wheel 1055120 Oct 6 01:28 /usr/local/sbin/nginx
- Versions: the ports build is nginx 1.28.0, and the package is 1.30.6. The mittens are newer, because our ports tree is older than the package repository. A glove is only as fresh as the tree it was cut from.
- Modules: -V shows the build's configure options. The ports build has --with-http_degradation_module and none of the modules we removed. The package has http_v3, http_dav, http_flv, http_mp4 and mail_ssl built in.
- Size: the custom binary is 1,055,120 bytes against 1,296,512 for the package: 19 percent smaller, because the features we dropped are not in it.
β οΈ Newer package repository (checked October 2026): the package repository now targets FreeBSD 15.1, while 15.0-RELEASE is end of life. On a 15.0 machine pkg may stop with Newer FreeBSD version for package ... [y/N], and the version numbers in the output below may differ from yours. To carry on, run export IGNORE_OSVERSION=yes first, or better, move to a supported release (see the upgrading guide).
root@practice:~ # pkg info nginx | grep -E 'Version|Origin|HTTP_DAV|HTTP_FLV|HTTP_MP4|MAIL |HTTPV3|HTTP_DEGRADATION|WWW ' Version : 1.28.0_3,3 Origin : www/nginx WWW : https://nginx.com/ Comment : Robust and small WWW server HTTPV3 : off HTTPV3_BORING : off HTTPV3_LSSL : off HTTPV3_QTLS : off HTTP_DAV : off HTTP_DAV_EXT : off HTTP_DEGRADATION: on HTTP_FLV : off HTTP_MP4 : off MAIL : off WWW : off root@practice:~ # pkg -j web info nginx | grep -E 'Version|Origin|HTTP_DAV|HTTP_FLV|HTTP_MP4|MAIL |HTTPV3|HTTP_DEGRADATION|WWW ' Version : 1.30.6,3 Origin : www/nginx WWW : https://nginx.com/ Comment : Robust and small WWW server HTTPV3 : on HTTPV3_BORING : off HTTPV3_LSSL : off HTTP_DAV : on HTTP_DAV_EXT : off HTTP_DEGRADATION: off HTTP_FLV : on HTTP_MP4 : on MAIL : on WWW : on root@practice:~ # /usr/local/sbin/nginx -t nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful root@practice:~ # whereis nginx nginx: /usr/local/sbin/nginx /usr/local/share/man/man8/nginx.8.gz /usr/ports/www/nginx
pkg info records the build options of each: on the host, HTTP_DAV: off, HTTP_DEGRADATION: on, WWW: off; in the jail, the reverse. And the custom build passes its own syntax test (nginx -t). whereis nginx finds both the program and the port it came from: /usr/ports/www/nginx.
Tidying up
root@practice:~ # pkg autoremove -n Updating database digests format: 0%Updating database digests format: 0%Updating database digests format: 100% Checking integrity... done (0 conflicting) Deinstallation has been requested for the following 15 packages: Installed packages to be REMOVED: bsddialog: 1.1 gettext-runtime: 1.0_1 gettext-tools: 0.23.1_1 gmake: 4.4.1 help2man: 1.49.3_1 indexinfo: 0.3.1_1 libiconv: 1.17_1 libtextstyle: 0.23.1 p5-Locale-gettext: 1.07 p5-Locale-libintl: 1.35 p5-Text-Unidecode: 1.30 p5-Unicode-EastAsianWidth: 12.0 perl5: 5.42.0_1 pkgconf: 2.4.3,1 texinfo: 7.1_12,1 Number of packages to be removed: 15 The operation will free 103 MiB. root@practice:~ # du -sh /usr/ports/distfiles 42M /usr/ports/distfiles root@practice:~ # ls /usr/ports/distfiles Text-Unidecode-1.30.tar.gz gettext-0.23.1.tar.xz libintl-perl-1.35.tar.gz perl tree-2.2.1.tgz Unicode-EastAsianWidth-12.0.tar.gz gettext-1.07.tar.gz m4-1.4.19.tar.xz pkgconf-2.4.3.tar.xz autoconf-2.72.tar.xz help2man-1.49.3.tar.xz nginx-1.28.0.tar.gz portconfig-0.6.2.tar.bz2 bsddialog-1.1.tar.bz2 libiconv-1.17.tar.gz pcre2-10.46.tar.bz2 texinfo root@practice:~ # ls /usr/ports/www/nginx Makefile Makefile.extmod Makefile.options.desc distinfo files pkg-descr pkg-plist
- Compiling leaves build-time tools behind. pkg autoremove -n (the -n means "only tell me") lists 15 packages, perl5, texinfo, gmake and friends, that nothing needs at runtime any more: 103 MiB. Remove them with pkg autoremove when you are sure.
- /usr/ports/distfiles keeps every source archive you downloaded: 42 MB here. make distclean in a port folder deletes its source archive, and make clean removes its work folder (the clean in make install clean does this for you).
Which one should you wear?
Wear mittens (pkg) most of the time. They are quick, they are tested together, and updating is pkg upgrade. Reach for gloves (ports) when:
- you need an option or module that the package does not have (or want to remove ones you do not need);
- you want to patch the software or build a specific version;
- the software is not offered as a package at all;
- you are tuning for specific hardware.
The price of gloves: build time, disk space, the build tools left behind, and you are now responsible for rebuilding when security fixes arrive. Two warnings (from the documentation, not run here): pkg upgrade can replace your custom build with the repository's package when it finds a newer version, so lock the package with pkg lock nginx if you want to keep your gloves; and a big fleet should not build on every server. poudriere builds your own package repository from the ports tree in a clean jail, once, and all your servers then install those custom packages with plain pkg: gloves made once, worn everywhere. The poudriere page does exactly that.
On Linux, the nearest ideas are Gentoo's emerge and Arch's AUR for building, and rpmbuild or apt source for rebuilding a distribution package. On FreeBSD it is simply how the system is built.
β οΈ Do not build on a production server that is short of memory or disk. A big port (a browser, a database) can take hours and gigabytes. Build in a virtual machine or with poudriere, and put the finished package on the server.
The package was one size fits all,<br>
and his hands are not.
π§ͺ Try it yourself
In a FreeBSD virtual machine:
1. Get a ports tree into /usr/ports (from the install disc, or with git). 2. Look at the recipe of a small port such as sysutils/tree, and ask it for its version with make -V PORTVERSION. 3. Run make config in it and look at the options dialog. 4. List its build dependencies, install them as packages with make install-missing-packages, and build it with make install clean BATCH=yes. How long did it take? 5. Check it with pkg info: what is its Origin? 6. Put an option for another port into /etc/make.conf and check it with make showconfig. 7. Compare a ports build with the package version: -V flags if the program has them, and the file sizes. 8. Run pkg autoremove -n. What was left behind?
The cheat sheet
- Tree: /usr/ports/CATEGORY/PORT. Get it with git clone (or from the install disc). whereis NAME finds a program's port.
- Look: make -V VARIABLE, make showconfig, make build-depends-list, make run-depends-list.
- Options: make config (dialog), make config-recursive (with dependencies), saved under /var/db/ports. In /etc/make.conf: category_port_SET=OPTION, category_port_UNSET=OPTION, CPUTYPE?=native.
- Build: make install-missing-packages (dependencies as packages), make install clean BATCH=yes.
- Tidy: pkg autoremove -n, make clean, make distclean.
- After: pkg info NAME (shows Origin and Options), pkg lock NAME, pkg which FILE.
- At scale: poudriere builds your own package repository (see the poudriere page).
← The pf Firewall: Rules, Tables and Redirects · ↑ FreeBSD Corner · bhyve: Virtual Machines Built into FreeBSD →