Pin the base image first, then decide how to freeze packages installed on top of it. Options are a dated snapshot archive, explicit version pins, a private mirror, or building a base image once and reusing it. Choose by how often you can afford to maintain it.
The gap pinning the base leaves
A base image pinned by digest does not move. But a Dockerfile step that installs packages usually asks a package index for the current version, and the index changes. Docker's build guidance says to combine updating the package index and installing in one instruction, which avoids a stale cached index, but it does not make the versions repeatable. Two builds a month apart can install different versions of the same package with the same Dockerfile.
Four approaches
| Approach | How | Pros | Cons |
|---|---|---|---|
| Snapshot archive | Point the package manager at a dated copy of the repository | Recreates a past state without hosting anything | Depends on an outside service, snapshots are large |
| Explicit version pins | Name exact versions in the install command | Simple, visible in the diff | Old versions can vanish from the live repository |
| Private mirror | Keep copies you control | Survives upstream removal | You run and secure a service |
| Prebuilt base image | Build an image with packages once, pin it by digest | One place to update, fast builds | You own a second image and its patch cycle |
For Debian systems, the reproducible-builds documentation says Debian packages must be reproducible with the packages that were available in the archive when they were built, and that you can use the project's snapshot site to create a system in that state. The snapshot site describes itself as a wayback machine for the Debian archive.
Matching the approach to the team
- Team that rebuilds rarely: pin versions and accept that you may have to bump them when an old version disappears.
- Team that must rebuild old releases: use a snapshot or a mirror, and record the snapshot date in the build record.
- Team with many services sharing the same packages: build one base image, pin by digest, and update it on a schedule with scanning before and after.
Keep the patch path open
The danger of freezing is staleness. Whichever approach you choose, set a date to refresh it, and scan the result. A pinned package list that nobody revisits is how known flaws survive in production for years.
A simple routine:
- Monthly, or when an urgent advisory appears, regenerate the package set.
- Scan the new image and compare against the old.
- Run tests, then promote the new digest.
- Record the change in the build record.
Do not pin what you can remove
Every package in a build image is something to maintain. Docker's guidance recommends avoiding installing packages you do not need. A smaller image has fewer things to pin, scan and patch. Move compilers and build tools into a build stage and keep them out of the final image where you can.
Try it yourself
Click through it at your own pace, or open it full screen.
Look at how operating system packages inside an image are listed and checked. That list is what your pinning strategy controls.
Where Safeguard fits
Safeguard scans container images and the packages inside them, and can open fix pull requests that stay off until you turn them on, with every category on Manual. Free scan: gold.safeguard.sh/scan.
Quick check
Is pinning package versions the same as pinning the image?
No. The image pin fixes the base. Packages installed afterwards need their own control.
Do I need a snapshot archive for non-Debian systems?
Not necessarily. Other systems offer different mechanisms, so check what your distribution provides.
Sources
- Docker docs: Dockerfile build guidance
- Reproducible Builds: Volatile inputs can disappear
- snapshot.debian.org
Last reviewed: 19 October 2026.