2.7 KiB
2.7 KiB
name, description
| name | description |
|---|---|
| docker-pin-latest-census | Prove a Docker Hub / lscr.io image pin is the latest version before bumping it: registry-API tag enumeration, the rolling-latest trap, digest pinning, and rollback proof. |
Use when asked whether a Docker Hub / lshr.io image pin is the latest version before bumping it, or when a manifest claims an image is stale. Registry API is the answer; the latest tag is not.
The API
# hub: token then paginate (the list is 100 per page)
tok=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull" | jq -r .token)
for off in 0 100 200 300 400 500; do
curl -s -H "Authorization: Bearer $tok" \
"https://hub.docker.com/v2/repositories/${repo}/tags?page_size=100&ordering=last_updated&page=${page}" \
| jq -r '.results[] | "\(.name)\t\(.last_updated)"'
done | sort
# lscr.io (linuxserver): tags are semver, `latest` is a rolling alias
curl -s https://images-api.github.com/v2/linuxserver/${repo}/tags/list # wrong host for lscr
curl -s "https://hub.docker.com/v2/repositories/linuxserver/${repo}/tags?page_size=100" | jq -r '.results[].name'
Docker Hub's own tag list (hub.docker.com/v2/repositories/<repo>/tags, ordered by last_updated) is the practical source. The registry API (/v2/<repo>/tags/list) needs the bearer token flow above; both return tags, neither sorts by semver.
The latest trap
latestis a rolling alias, not a version. "Pin is older thanlatest" says nothing about whether a versioned tag exists.latestcan lag or be rebuilt without a version bump; comparing a digest pin against it proves nothing.- For linuxserver images:
stableis the rolling branch and version tags (2026-09-30) are what you pin; multi-arch manifest lists mean the same tag has per-arch digests — the pin must be the manifest-list digest (RepoDigests[0]) you actually pull, or a re-pull on another arch silently changes the image.
Verification recipe (before bumping a pin)
- Read the pinned tag and digest from the manifest/quadlet.
- Enumerate tags from the registry API (paginate;
last_updatedis not semver order). - Compare versions, not recency: does a version greater than the pinned one exist?
- If bumping: pull the new tag and record
docker/podman inspect --format '{{index .RepoDigests 0}}'as the new digest — pin the digest, not the tag. - Prove the service: it starts, version endpoint answers the new version, no crash-loop, and the rollback is the previous digest.
Report
"pinned: X (digest …), latest available: Y, upstream changelog: …" — or "pin is current: no version tag newer than X exists". Never claim staleness from the latest tag alone.