Safeguard
Developer Tools•

Why does the same commit build a different Docker image?

The same commit gives a different Docker image when tags move, package indexes change or builds write timestamps. How to find each cause and what to pin.

Safeguard Security Team
4 min read

The same commit builds a different Docker image because the Dockerfile reads things that change: a base image tag that moved, packages fetched at build time, files with new timestamps, or build steps that write the date. Pin the base by digest, control package sources and fix times from the commit.

Four causes, in order of how often they bite

1. The base image tag moved. Docker's guidance says tags are mutable, so a tag can resolve to a different patch release when you rebuild months later. You then have no audit trail of the exact version in use.

2. Packages were fetched at build time. An instruction that updates the package index and installs packages will pick up whatever the index offers that day. The Debian project runs a snapshot archive that works like a wayback machine for its packages, and the reproducible-builds project points to it as a way to recreate a system in the state of a past archive. Whether you use that or an internal mirror, the idea is the same: make the package source stop moving, or accept that it moves.

3. Timestamps got written. The reproducible-builds documentation calls timestamps the biggest source of reproducibility issues. Filesystems and archive formats record modification times on top of any timestamps the tools write.

4. File order and metadata varied. Archives built from directory listings can differ because filesystems do not guarantee listing order. The same documentation shows sorting names in a locale independent way and fixing modification times.

A quick diagnosis

  1. Build the image twice on the same machine with the cache disabled.
  2. If the layers differ, find the first layer that differs. Layers are built in order, so everything after it will differ too.
  3. Compare the contents of that layer, not just the hashes. Listing files with their times and sizes usually shows the culprit.
  4. Fix that cause, then repeat.
docker build --no-cache -t test:a .
docker build --no-cache -t test:b .
docker history test:a
docker history test:b

Pinning versus the build cache

A warm cache can hide the problem. Docker steps through each instruction and reuses a cached result when it can, so a second build on your machine may look identical simply because it reused layers. Always test with the cache off, and remember that a cache hit on one runner and a miss on another is itself a way for two builds to differ.

What to decide

ChoiceResult
Pin base image by digestSame base every time, you own the update
Pin package versions or use a snapshot sourceSame packages, more maintenance
Accept moving packages, rebuild weeklyFresher patches, images differ between builds
Set timestamps from the commitRemoves the biggest source of noise

Most small teams should do the first and last, and make a deliberate choice on the middle two.

Where Safeguard fits

Safeguard scans container images, so you can compare findings between two builds of the same commit and see what actually moved. Try a free scan at gold.safeguard.sh/scan.

Quick check

Is a different image hash always a problem?

Not always. It matters when you need to prove what you shipped or roll back to an identical artifact. Know which differences are harmless, such as timestamps, and write them down.

Does turning off the cache make the build reproducible?

No. It helps you see the true differences. You still need to pin the inputs.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.