pkg on FreeBSD: Package Manager Review
pkg (also known as pkgng) is FreeBSD's official binary package manager. It replaced the older pkg_add/pkg_delete tools in FreeBSD 10.0 and has been the standard package management interface since. pkg handles installation, removal, upgrading, and dependency resolution for binary packages built from the FreeBSD Ports Collection. It is fast, reliable, and designed with the FreeBSD philosophy of doing one thing well.
This review covers pkg's architecture, essential commands, repository configuration, security auditing, package locking, comparison with apt (Debian/Ubuntu), dnf (Fedora/RHEL), and pacman (Arch Linux), and guidance on when to use the Ports Collection instead.
pkg Architecture
pkg is a standalone binary package manager written in C. It uses a SQLite database to track installed packages and their metadata, stored at /var/db/pkg/local.sqlite. Remote package repositories also use SQLite databases for metadata.
The architecture has several key components:
Package format: FreeBSD packages are compressed tar archives (txz or tzst) containing the compiled software, metadata (dependencies, description, scripts), and installation instructions. The naming convention is name-version.pkg.
Repository catalog: A SQLite database published by the FreeBSD package build infrastructure. When you run pkg update, this catalog is downloaded and stored locally at /var/db/pkg/repo-FreeBSD.sqlite.
Dependency solver: pkg uses a SAT (Boolean satisfiability) solver for dependency resolution. When you install a package, the solver determines the complete set of dependencies needed and resolves conflicts.
Package building: Binary packages are built by the FreeBSD project using Poudriere, a bulk package builder that compiles each port in a clean jail environment. The official repository provides packages built with default options.
Essential Commands
Installation
sh# Install a package pkg install nginx # Install multiple packages pkg install nginx postgresql16-server redis # Install without confirmation prompt pkg install -y vim # Install from a specific repository pkg install -r custom_repo nginx # Dry run -- show what would be installed pkg install -n nginx
Removal
sh# Remove a package pkg delete nginx # Remove a package and its unused dependencies pkg autoremove # Remove all packages (nuclear option) pkg delete -a # Force removal ignoring dependencies pkg delete -f nginx
Upgrading
sh# Update repository catalog pkg update # Upgrade all installed packages pkg upgrade # Upgrade a specific package pkg upgrade nginx # Check for available upgrades without installing pkg upgrade -n # Force reinstall of a package pkg install -f nginx
Searching and Information
sh# Search for packages by name pkg search nginx # Search with description pkg search -D "web server" # Show package information pkg info nginx # Show installed files for a package pkg info -l nginx # Show dependencies pkg info -d nginx # Show reverse dependencies (what depends on this) pkg info -r openssl # List all installed packages pkg info # Show package origin (port it was built from) pkg info -o nginx
Querying
sh# Which package owns a file pkg which /usr/local/sbin/nginx # List files that would be installed pkg fetch -d nginx && pkg info -l nginx # Show packages installed manually (not as dependencies) pkg query -e '%a = 0' '%n-%v' # Show automatically installed packages (dependencies only) pkg query -e '%a = 1' '%n-%v' # List packages by size pkg query '%sb %n' | sort -rn | head -20
Repository Configuration
Default Repository
FreeBSD's default package repository is configured at /etc/pkg/FreeBSD.conf. Custom repositories go in /usr/local/etc/pkg/repos/.
View the current repository configuration:
shpkg -vv
Custom Repository
Create /usr/local/etc/pkg/repos/custom.conf:
sh# /usr/local/etc/pkg/repos/custom.conf custom: { url: "http://pkg.example.com/packages/${ABI}", mirror_type: "srv", signature_type: "pubkey", pubkey: "/usr/local/etc/pkg/repos/custom.pub", enabled: yes, priority: 10 }
Switching to Latest Branch
By default, FreeBSD uses the quarterly branch (stable, updated quarterly). To use the latest branch (rolling updates):
shmkdir -p /usr/local/etc/pkg/repos cat > /usr/local/etc/pkg/repos/FreeBSD.conf << 'EOF' FreeBSD: { url: "pkg+http://pkg.FreeBSD.org/${ABI}/latest", mirror_type: "srv", signature_type: "fingerprints", fingerprints: "/usr/share/keys/pkg", enabled: yes } EOF pkg update -f
Building Custom Packages with Poudriere
When you need packages with non-default options, build them locally with Poudriere:
shpkg install poudriere # Create a jail (build environment) poudriere jail -c -j 14amd64 -v 14.1-RELEASE -a amd64 # Create a ports tree poudriere ports -c -p default # Configure port options poudriere options -j 14amd64 -p default www/nginx # Build packages poudriere bulk -j 14amd64 -p default www/nginx databases/postgresql16-server
The built packages go to /usr/local/poudriere/data/packages/14amd64-default/. Point a custom repository configuration at this directory.
Security Auditing
pkg includes built-in vulnerability auditing that checks installed packages against the VuXML (Vulnerability and eXposure Markup Language) database maintained by the FreeBSD Security Team.
Auditing Packages
sh# Check for known vulnerabilities pkg audit -F # The -F flag fetches the latest vulnerability database # Run without -F to use the cached database pkg audit
Output shows each vulnerable package, the CVE identifiers, and links to the vulnerability description.
Automated Auditing
Add to /etc/periodic.conf for daily vulnerability checks:
sh# /etc/periodic.conf daily_status_security_pkgaudit_enable="YES"
FreeBSD's periodic system will run pkg audit daily and include results in the daily security email.
Package Integrity Verification
sh# Verify checksums of installed files pkg check -s -a # Verify dependencies are satisfied pkg check -d -a # Recompute and fix dependency database pkg check -B -a
Package Locking
Locking prevents a package from being modified by upgrade or delete operations. This is useful for packages you have customized or that you need to pin to a specific version.
sh# Lock a package pkg lock nginx # Unlock a package pkg unlock nginx # List locked packages pkg lock -l # Lock a package by pattern pkg lock 'php*'
When a locked package has an available upgrade, pkg upgrade will skip it and warn you. Dependencies that require the newer version will not be upgraded either.
Package Creation
You can create packages from installed software or from scratch:
sh# Create a package from an installed port pkg create nginx # Create a package from a manifest pkg create -M ./manifest.ucl -o /tmp/packages/ # Create packages for all installed software pkg create -a -o /tmp/packages/
This is useful for deploying identical software configurations across multiple FreeBSD servers without compiling from source on each one.
pkg vs apt vs dnf vs pacman
apt (Debian/Ubuntu)
Architecture: apt uses dpkg as its low-level package installer and provides dependency resolution on top. Packages are .deb files. Repository metadata uses Packages.gz files with a flat text format.
Strengths over pkg: apt has a larger package selection (Debian has ~60,000 packages vs FreeBSD's ~35,000 ports). apt-file can search for files across uninstalled packages. PPAs (Personal Package Archives) provide an easy way for third parties to distribute packages. apt has better handling of configuration file conflicts during upgrades.
Weaknesses: apt is slower for catalog updates (text-based metadata vs SQLite). The split between apt and dpkg adds complexity. Dependency resolution can be less deterministic than pkg's SAT solver for complex scenarios.
dnf (Fedora/RHEL)
Architecture: dnf uses RPM as its package format and libsolv for dependency resolution. Repository metadata is XML (repodata) or SQLite.
Strengths over pkg: dnf has modular streams for installing different versions of the same software. Rich dependencies (boolean expressions in requires/conflicts) are more expressive. dnf history allows full transaction rollback.
Weaknesses: dnf is heavier (Python-based) and slower to start. RPM spec files are more complex than FreeBSD port Makefiles. Module streams add conceptual complexity.
pacman (Arch Linux)
Architecture: pacman is a simple, fast package manager using tar archives (.pkg.tar.zst). It uses a local database in /var/lib/pacman/.
Strengths over pkg: pacman is extremely fast for all operations. The Arch User Repository (AUR) provides a massive collection of community-maintained build scripts. pacman's simplicity is a feature -- fewer abstractions, more predictable behavior.
Weaknesses: pacman has less robust dependency resolution (no SAT solver). Rolling releases mean every upgrade can potentially break things. No built-in security auditing like pkg audit.
Comparison Table
| Feature | pkg | apt | dnf | pacman |
|---|---|---|---|---|
| Speed | Fast | Moderate | Slow start | Very fast |
| Dependency solver | SAT solver | Heuristic | libsolv (SAT) | Simple |
| Package format | txz/tzst | .deb | .rpm | .pkg.tar.zst |
| Catalog format | SQLite | Text | XML/SQLite | Text |
| Security audit | Built-in | Separate (debsecan) | Separate (yum-security) | None built-in |
| Config handling | None (replaces) | Prompt/merge | .rpmnew/.rpmsave | .pacnew |
| Rollback | No | No | Yes (history) | No |
| Package count | ~35,000 | ~60,000 | ~30,000 | ~15,000 + AUR |
When to Use the Ports Collection
pkg installs binary packages built with default options. Sometimes you need more control.
Use ports when:
- You need non-default compile-time options (e.g., nginx with specific modules)
- You need a version not yet in the binary repository
- You need to apply custom patches
- You are building a custom package repository with Poudriere
- You need a package compiled with specific CPU optimizations
Use pkg when:
- Default options are acceptable
- You want fast installation
- You are managing many servers and need consistency
- You want security updates without recompilation
Working with Ports
sh# Install the ports tree pkg install git git clone https://git.FreeBSD.org/ports.git /usr/ports # Or use portsnap (deprecated but still functional) portsnap fetch extract # Build a port cd /usr/ports/www/nginx make config # choose options make install clean # Search ports make -C /usr/ports search name=nginx # Update ports tree cd /usr/ports && git pull
The best practice for organizations is to use Poudriere to build custom packages from ports and distribute them via a private pkg repository. This gives you the compile-time control of ports with the deployment simplicity of pkg.
FAQ
How do I bootstrap pkg on a fresh FreeBSD install?
On a fresh FreeBSD installation, run any pkg command and it will prompt to bootstrap itself:
shpkg update
Answer yes to install pkg. Alternatively, install it explicitly:
sh/usr/sbin/pkg bootstrap -f
How do I see what changed in an upgrade before applying it?
Use the dry-run flag:
shpkg upgrade -n
This shows exactly which packages will be upgraded, added, or removed, along with the download size and disk space changes.
How do I hold back a specific package version?
Lock the package:
shpkg lock nginx
This prevents pkg upgrade from modifying it. Remember to unlock when you are ready to upgrade.
Can pkg handle configuration files during upgrades?
pkg does not merge configuration files. If a package upgrade includes a new configuration file, pkg will either overwrite the existing file or install the new one with a .sample suffix, depending on how the port handles it. Check /usr/local/etc/ after upgrades for .sample files and merge changes manually. This is one area where apt's configuration handling is superior.
How do I clean up unused packages?
sh# Remove packages that were installed as dependencies but are no longer needed pkg autoremove # Clean the package cache pkg clean # Clean all cached packages (including current versions) pkg clean -a
How do I downgrade a package?
pkg does not natively support downgrading. To downgrade, you must either install from a specific package file or use an older repository:
sh# If you have the old package file pkg install /path/to/nginx-1.24.0.pkg # Or fetch from an archived repository pkg install -r archived_repo nginx
Alternatively, build the specific version from ports.
How often should I run pkg audit?
Run pkg audit -F at least weekly. Enable the daily periodic check (daily_status_security_pkgaudit_enable="YES" in /etc/periodic.conf) for production servers. When a vulnerability is reported, update the affected package immediately with pkg upgrade package_name.