Docker Image Digest

A few moons ago I built a simple tool called RedisAdministrator. I used to just push changes to master because it was future Carls problem and then build the docker image tagged latest.

This was a 💩 strategy as the changes should be versioned and cater for breaking changes. Current Carl fixed it, you can see v1.0.0 here ❤️

One thing I did change was moving over to the options pattern for config instead of injecting values as environmental variables. This broke a long standing way of starting the application but I noticed that consumers were still working. I did not understand why until I looked at the value being used. It was carlpaton/redis-administrator:latest@sha256:529a5813d33a2b069bac480a4eba9a0bdfabab327c81b8332959f31ee625ab4e

The SHA is a Docker Hub SHA, more accurately called a Docker Image Digest. It is not a Git commit SHA as I initially thought.

The bit I was missing is that latest and @sha256:... are not equivalent. latest is a mutable tag that can move to a newer image, while the digest points at one exact image manifest. That means a consumer pinned to @sha256:529a... can keep pulling that old image even though latest now points somewhere else.

Commands that prove it

Here are the Docker commands and outputs I ran while updating this post.

Pull the image by tag:

1
docker pull carlpaton/redis-administrator:latest
1
2
3
Digest: sha256:056a83a595d606286c0b60748a18e86b3536c2afbbfedf4d0656265c5fe88849
Status: Image is up to date for carlpaton/redis-administrator:latest
docker.io/carlpaton/redis-administrator:latest

Inspect the local image and show the repo digest:

1
docker image inspect carlpaton/redis-administrator:latest --format '{{index .RepoDigests 0}}'
1
carlpaton/redis-administrator@sha256:056a83a595d606286c0b60748a18e86b3536c2afbbfedf4d0656265c5fe88849

This is why you can see a full image reference like:

1
carlpaton/redis-administrator:latest@sha256:056a83a595d606286c0b60748a18e86b3536c2afbbfedf4d0656265c5fe88849

That is the current state of latest, but the older digest from my original note still exists in Docker Hub:

1
docker pull carlpaton/redis-administrator@sha256:529a5813d33a2b069bac480a4eba9a0bdfabab327c81b8332959f31ee625ab4e
1
2
3
4
5
6
7
8
9
10
docker.io/carlpaton/redis-administrator@sha256:529a5813d33a2b069bac480a4eba9a0bdfabab327c81b8332959f31ee625ab4e: Pulling from carlpaton/redis-administrator
2632bccab119: Pull complete
8b01d789a397: Pull complete
fc7181108d40: Pull complete
516f05c8caee: Pull complete
88b297f96aab: Pull complete
f523c3817e30: Pull complete
Digest: sha256:529a5813d33a2b069bac480a4eba9a0bdfabab327c81b8332959f31ee625ab4e
Status: Downloaded newer image for carlpaton/redis-administrator@sha256:529a5813d33a2b069bac480a4eba9a0bdfabab327c81b8332959f31ee625ab4e
docker.io/carlpaton/redis-administrator@sha256:529a5813d33a2b069bac480a4eba9a0bdfabab327c81b8332959f31ee625ab4e

That is the key point: latest has moved to 056a..., but the older 529a... manifest is still available and can still be pulled directly.

The exact combined reference from the post also still resolves today:

1
docker pull carlpaton/redis-administrator:latest@sha256:529a5813d33a2b069bac480a4eba9a0bdfabab327c81b8332959f31ee625ab4e
1
2
3
4
docker.io/carlpaton/redis-administrator@sha256:529a5813d33a2b069bac480a4eba9a0bdfabab327c81b8332959f31ee625ab4e: Pulling from carlpaton/redis-administrator
Digest: sha256:529a5813d33a2b069bac480a4eba9a0bdfabab327c81b8332959f31ee625ab4e
Status: Image is up to date for carlpaton/redis-administrator@sha256:529a5813d33a2b069bac480a4eba9a0bdfabab327c81b8332959f31ee625ab4e
docker.io/carlpaton/redis-administrator:latest@sha256:529a5813d33a2b069bac480a4eba9a0bdfabab327c81b8332959f31ee625ab4e

If you want to prove these are different images, inspect both digests:

1
2
docker image inspect carlpaton/redis-administrator:latest --format '{{.Id}}|{{.Created}}|{{index .RepoDigests 0}}'
docker image inspect carlpaton/redis-administrator@sha256:529a5813d33a2b069bac480a4eba9a0bdfabab327c81b8332959f31ee625ab4e --format '{{.Id}}|{{.Created}}|{{index .RepoDigests 0}}'
1
2
sha256:056a83a595d606286c0b60748a18e86b3536c2afbbfedf4d0656265c5fe88849|2025-07-03T09:56:13.587152976Z|carlpaton/redis-administrator@sha256:056a83a595d606286c0b60748a18e86b3536c2afbbfedf4d0656265c5fe88849
sha256:529a5813d33a2b069bac480a4eba9a0bdfabab327c81b8332959f31ee625ab4e|2019-07-02T01:23:45.087660991Z|carlpaton/redis-administrator@sha256:529a5813d33a2b069bac480a4eba9a0bdfabab327c81b8332959f31ee625ab4e

So the consumer kept working because they were effectively pinned to the 2019 image digest, not because latest was still pointing at that image. Today latest resolves to the 2025 image digest instead.

If you see a different digest for latest in future, that is expected. latest is mutable, while @sha256:... is immutable.