pkg-check-update(1)
NAME
pkg-check-update - report whether updates are available, changing nothing
SYNOPSIS
pkg check-update [--exact] [--provisional] [--no-cache] [name...]
pkg check-updates [--exact] [--provisional] [--no-cache] [name...]
DESCRIPTION
Is there anything to upgrade? That's the one question this command answers, and it changes nothing while doing so. No manifest is installed, no payload is fetched, no state is written.
Why not just use upgrade --dry-run?
You could. pkg upgrade --dry-run already tells you what would change. But it pays for a full index build to get there: every repository's manifests, one request per manifest, tens of megabytes against a chain the size of Base plus Common. That's fine when you're about to upgrade anyway. It's way too much for a question asked on a timer, where the answer is usually "no".
So by default this command diffs pin maps instead. A pin map is the list a repository publishes of which build is current for each name, and it's a few kilobytes. That gets compared against what's installed. And if a repository's timestamp.json still names the snapshot the metadata cache saw last time, it stops right there. On the common nothing-changed path, one small signed request per repository is the entire cost.
It opens the state read-only and needs no privileges. That's the whole point: a desktop applet or an operator's timer can run it.
Without names, every installed package is considered. Naming packages limits the check to them, and a name that isn't installed is an error, not an empty answer.
Four answers, not a count
Each installed name lands in one of these:
available- The chain pins a different N-V-R than the one installed.
held- No repository in the chain pins the name at all. It's reported, never dropped, and never guessed at. A package that just moved out of the chain must not look the same as one that's current.
- held by pin
- A local pin (pkg-pins.d(5)) holds the name at a build the chain has moved past. It's listed with both builds and the pins file, and isn't counted as available: applying it means releasing the pin, not running an upgrade.
current- The installed N-V-R equals the pin. Not printed.
Can I trust the cheap answer?
Mostly. But it's advice, and you should know where it's blind.
Convergence is about payload-digest identity, not about comparing N-V-Rs; see pkg-upgrade(1). A pin diff only compares N-V-Rs. So it can't see a rebuild at the same N-V-R whose bytes changed, a subpackage the new build sheds, or a dependency a package gained.
--exact resolves the plan pkg-upgrade(1) would apply, and that's the authoritative answer. The two tiers measure different things, not the same thing with different accuracy. That's why the output names the tier that produced it.
A repository that published no pin file can't be answered for cheaply. Rather than report its names as held, which reads as "orphaned" when it really means "not asked", it's named on stderr:
warning: Common published no pin file; names only it answers for are reported as held. Use --exact for an authoritative answer.
OPTIONS
--exact- Resolve the full plan instead of diffing pins. It's authoritative: it sees dependency closures, shed subpackages and payload digests. But it costs a complete index build, which is exactly what the default exists to avoid. On this tier the held list is always empty, and a dependency the plan would newly install shows up with
-as its installed side. --provisional- Compare against the staged provisional pins (metadata/provisional_pins.json) instead of the blessed ones (metadata/pins.json), on either tier. Only a repository read from a local directory can have both files; a remote repository serves the staged pins either way. See pkg-chain(7).
--no-cache- Ignore the cached metadata and fetch every repository's chain again. A cache hit is already signature-verified, so this is for debugging the cache itself, not for trusting the answer more. It only affects the pin tier.
All global options apply; see pkg(1).
OUTPUT
One line per available update, installed state on the left:
openssl 3.5.6-13 -> 3.5.6-14 [Common] vim 9.1.1848-2 -> 9.1.1902-1 [Common]
The pin tier names the repository, because it knows which one answered. --exact resolves through the whole chain and doesn't attribute any single one, so it prints just the arrow:
openssl 3.5.6-13 -> 3.5.6-14
Held packages follow, after a blank line:
2 package(s) held -- no repo in the chain pins them: oldpkg 1.2-3 gonepkg 4.5-6
Then names a local pin holds:
1 package(s) held by a local pin (pkg unpin to release): curl held by pin at 8.16.0-1 (Base has 8.17.0-1) -- /etc/pkg/pins.d/local.toml
When nothing is available, the output ends with:
no updates available
JSON
{ "ok": true, "tier": "pins", "updates_available": true, "available": [ { "name": "openssl", "installed": "3.5.6-13", "available": "3.5.6-14", "repo": "Common" } ], "held": [ { "name": "oldpkg", "installed": "1.2-3", "available": null, "repo": null } ], "held_by_pin": [ { "name": "curl", "pinned": "8.16.0-1", "chain": "8.17.0-1", "repo": "Base", "file": "/etc/pkg/pins.d/local.toml" } ] }
tier is pins or exact. repo is null on the exact tier, and available is null for a held row. held_by_pin lists pinned names the chain has moved past; chain and repo are null when no repository pins the name anymore.
EXAMPLES
Is there anything to do?
pkg check-update
Asking about one package
pkg check-update openssl pkg check-update openssl kernel vim
The authoritative answer
pkg check-update --exact
A timer that only speaks up when there's something to say
pkg check-update >/tmp/updates || cat /tmp/updates
The || fires on any non-zero exit: 100 when updates exist, and 1 when the check itself failed. Both are worth hearing about.
A monitoring check
pkg check-update >/dev/null case $? in
- echo "OK: up to date" ;;
- echo "WARNING: updates available" ;;
*) echo "UNKNOWN: could not tell" ;; esac
Scripting
pkg --json check-update | jq -r '.available[].name' pkg --json check-update | jq -e '.updates_available' >/dev/null pkg --json check-update | jq -r '.held[] | "(.name) (.installed)"'
The everyday loop
pkg check-update && exit 0 pkg stage-update pkg upgrade
Testing a staged promotion
pkg check-update --exact --provisional
Debugging the metadata cache
pkg check-update --no-cache pkg -v check-update --no-cache
Where does the time go?
pkg -v check-update pkg -v check-update --exact
The -v line names the tier and the counts:
check-update [pins]: 2 available, 0 held
EXIT STATUS
This is the one pkg command that uses its exit code to carry the answer. It follows the dnf convention, so an existing timer or monitoring check works unchanged:
0- No updates available.
100- Updates are available.
1- Couldn't tell: unreadable state, an unreachable repository, a named package that isn't installed.
Held packages alone don't make it 100, and neither do names held by a pin. A held package isn't an update.
SEE ALSO
pkg(1), pkg-stage-update(1), pkg-upgrade(1), pkg-list(1), pkg-pin(1), pkg-pins.d(5), pkg-state(5), pkg-chain(7), pkg-repository-layout(7)
docs/design/repo.md, docs/design/staged-promotion.md, pkg-cli/JSON.md, pkg-cli/VERBOSITY.md