FreeBSD pkg State Operations: Inventory Roots, Dependencies, and Orphans
Audit installed FreeBSD package roots, automatic dependency flags, locks, reverse dependencies, and autoremove plans without surprise removals.
The pkg database records more than package names and versions. It also tracks dependency relationships, whether a package was installed automatically as a dependency, lock state, repository information, and files owned by a package. Using those fields deliberately makes package cleanup safer and helps operators distinguish intentional software from an orphaned dependency.
This guide is about installed package state and cleanup planning, not selecting between ports and binary packages, building a custom repository, or performing a security audit. The central operational rule is to review the proposed removal set before changing it. A dependency can be required by a service even if no one remembers installing it directly, and a lock is a local safeguard rather than a fleet-wide package policy.
Capture a baseline inventory
Record the FreeBSD release and the installed package list before maintenance:
freebsd-version -kru
pkg -v
pkg info
pkg stats
Save the output to a controlled change record or configuration-management artifact. Package name and version alone can be insufficient to distinguish a locally built package, alternate repository, or architecture variant. For a concise inventory, pkg query supports documented format keywords:
pkg query -a '%n|%v|%o|%a|%k|%R'
The fields are package name, version, origin, automatic-install flag, lock state, and recorded repository when available. A repository can be unknown for packages installed from a local file or a state where repository provenance was not recorded. Treat this as useful evidence, not a complete bill of materials or proof of current repository availability.
To list packages intentionally marked as roots, filter automatic status:
pkg query -e '%a = 0' '%n-%v (%o)'
To list packages marked automatic:
pkg query -e '%a = 1' '%n-%v (%o)'
The automatic flag is a package-manager bookkeeping attribute. It is not a semantic claim that an administrator never uses the package. A dependency may support a separately launched service, a script, a build pipeline, or an emergency recovery workflow. Review owners and service inventory before changing that flag.
Understand reverse dependencies and orphan status
Before removing a package, inspect what depends on it:
pkg query '%rn-%rv (%ro)' REPLACE_WITH_PACKAGE
pkg info -d REPLACE_WITH_PACKAGE
pkg info -r REPLACE_WITH_PACKAGE
Use an actual package name in place of the placeholder. pkg query’s evaluation variables can test dependency and reverse-dependency fields, while pkg info provides dependency information for a named installed package. Review the installed manual for exact field forms on the target pkg version. The package name is not always identical to its port origin, so use pkg info or pkg query to establish the correct identifier first.
An orphan is generally an automatically installed package that is no longer needed as a dependency according to pkg’s database. That definition is mechanical. It does not know whether an administrator manually invokes the binary, whether a local script calls it, or whether an operational procedure depends on it. Dependency metadata also may not model a program invoked dynamically by a configuration file.
Use dry-run mode before autoremove:
pkg autoremove -n
Read the entire proposed set and compare it with service owners, scheduled jobs, local scripts, and recovery documentation. Confirm that the change will not remove a library or tool required by a manually configured service. Only after the reviewed list is approved should an operator run the interactive removal command and capture the final transaction summary. Avoid -y for cleanup automation unless the exact candidate set is generated and reviewed by a separate controlled mechanism.
If the dry-run proposes a package that is intentionally used, determine why its automatic flag is set. Mark an intentional package as non-automatic through pkg’s documented set operation:
pkg set -A 0 REPLACE_WITH_PACKAGE
pkg query -e '%n = "REPLACE_WITH_PACKAGE"' '%n %a'
This changes package-manager metadata, not the package files or its dependency graph. Make the root selection reproducible in configuration management, because a reinstall or alternate host may not inherit the same local flag. If the package is not a root, do not simply relabel it to hide an incomplete dependency declaration in another package.
Apply package locks with narrow scope
pkg lock can protect installed packages from reinstallation, modification, or deletion by pkg. It does not generally block installing an unrelated new package, but an install or upgrade is blocked if resolving it would require modifying the locked package, such as changing its version. A pkg lock also does not enforce a version constraint in every other software-management tool. Use a lock for a documented exception, such as a locally patched package awaiting a scheduled replacement, not as a substitute for a supported repository or upgrade plan.
Inspect existing locks, lock only a named installed package, and verify the result:
pkg lock -l
pkg lock REPLACE_WITH_PACKAGE
pkg query -e '%n = "REPLACE_WITH_PACKAGE"' '%n %v locked=%k'
A lock can cause later upgrades or dependency changes to fail because pkg cannot modify the protected package. Track its owner, reason, date, replacement plan, and expiration condition. A lock without an owner or review date can turn into a hidden blocker that is discovered only during a major update.
When the reason is resolved, unlock the package explicitly and inspect the next proposed transaction:
pkg unlock REPLACE_WITH_PACKAGE
pkg lock -l
pkg upgrade -n
The final command is a plan, not an execution. Read the pkg-upgrade manual for the installed version and do not use -y until the transaction has been reviewed. The purpose of this article is package state, not a complete release-upgrade workflow; coordinate base-system and package repository compatibility separately.
Find files, ownership, and damaged registrations
When an executable or configuration file is unfamiliar, ask pkg which installed package owns it:
pkg which /usr/local/bin/REPLACE_WITH_PROGRAM
pkg info -l REPLACE_WITH_PACKAGE
pkg query '%n: %Fp' REPLACE_WITH_PACKAGE
Paths under /usr/local are commonly package-managed, but local software can also place files there. pkg which reports what the local database knows; it cannot prove that a file’s current contents are pristine. A checksum mismatch may reflect a local configuration edit, a replacement by an administrator, or unexpected drift. Preserve the file and compare it with the package manifest and local change record before repairing it.
pkg check can inspect installed-file checksums and dependencies, but its output requires interpretation:
pkg check -s -a
pkg check -d -a
The first checks package file checksums, and the second checks for missing dependencies according to pkg’s database. Run these checks in an approved diagnostic window if the system has many packages or expensive storage. Do not use automatic repair flags as a first step; capture mismatches and identify whether they are intended configuration files before restoring package contents.
For locally modified configuration, record which files are owned by packages and which are maintained by administrators. A package update may replace, preserve, or report configuration according to its manifest and scripts. A checksum discrepancy should not be “fixed” blindly if it would discard a local service configuration.
Make the review repeatable
For fleet maintenance, collect a machine-readable or stable tabular inventory from each host and compare it with a declared package set. Keep package origins, versions, automatic flags, lock states, repository identities, and the collection timestamp. Compare normalized output rather than terminal columns. If scripts parse pkg query output, pin the query format in the script and validate field counts and exit status.
Treat the package database as the host’s view of installed software, not proof that the service is running or healthy. A package can be installed but disabled; a program can be manually compiled outside pkg; and a running binary can differ from the package file if a local replacement was made. Correlate package state with service configuration, running processes, and deployment records before declaring inventory complete.
Plan removals as reversible operations where possible. Keep repository access and package archives available according to retention policy, record the exact transaction, and confirm that the relevant service still starts after the change. Package removal can run scripts and alter service state; review the package’s metadata and change window rather than treating autoremove as a harmless disk cleanup.
Acceptance criteria
A package-state cleanup is accepted when the complete candidate list has been reviewed, each retained root has an owner or declared purpose, locks have documented expiration criteria, and the final pkg transaction matches the approved plan. Confirm dependent services, scheduled jobs, and local scripts still function. Preserve pre-change and post-change inventories and any checksum or dependency diagnostics.
Avoid two opposite mistakes: keeping every automatic package forever just because its purpose is unclear, and deleting every orphan because pkg reports it. The correct action follows dependency data plus operational context. Use pkg’s machine-readable queries to expose state, then apply human review where package metadata cannot know the system’s workload.
Related:
- The FreeBSD Ports Collection vs. pkg: Choosing (and Combining) the Right Tool
- FreeBSD pkgbase: Managing the Operating System as Signed Packages
Sources:
- pkg-query(8), FreeBSD 15.1-RELEASE and Ports.quarterly
- pkg-autoremove(8), FreeBSD 15.1-RELEASE and Ports.quarterly
- pkg-lock(8), FreeBSD 15.1-RELEASE and Ports.quarterly
- pkg-set(8), FreeBSD 15.1-RELEASE and Ports.quarterly
- pkg-check(8), FreeBSD 15.1-RELEASE and Ports.quarterly
- pkg-info(8), FreeBSD 15.1-RELEASE and Ports.quarterly
- pkg-upgrade(8), FreeBSD 15.1-RELEASE and Ports.quarterly
- pkg-stats(8), FreeBSD 15.1-RELEASE and Ports.quarterly
- pkg-which(8), FreeBSD 15.1-RELEASE and Ports.quarterly