Docker image digest vs tag
Updated
A tag is a name that can move to another image. A digest is a SHA-256 hash of the image's content, so it can't. Use tags for people, and digests for what you deploy.
| Tag | Digest | |
|---|---|---|
| Looks like | api:1. | api@sha256:4f5a… |
| Can change | Yes: whoever can push can move it | No: other content has another digest |
| Readable | Yes | No |
| Use it for | Versions people talk about | Deploys, rollbacks, signing and promotion |
Why a tag isn't enough
api:1.4.2 usually means one image, but nothing enforces it. A tag can be pushed over, and if it moves during a rollout, Kubernetes warns that you can end up with Pods running a mix of old and new code. A digest pins what runs, whatever happens to the tags. Some registries can make tags immutable, which helps inside that registry and goes no further.
Find an image's digest
For an image in a registry, without pulling it:
docker buildx imagetools inspect nginx:1.27
crane digest nginx:1.27For an image you've pulled:
docker inspect --format='{{index .RepoDigests 0}}' nginx:1.27When you build it, take the digest from the build instead of looking it up afterwards. On GitHub Actions, docker/build-push-action gives it as an output:
- uses: docker/build-push-action@v7
id: build
with:
push: true
tags: registry.example/api:1.4.2
- run: echo "Built ${{ steps.build.outputs.digest }}"An image ID is what your local Docker shows in docker images. On older installs it's a local value that a registry can't serve. On the containerd image store, the default for fresh installs of Docker Engine 29, it matches the digest. Take the digest from the registry or the build either way.
Why one tag shows several digests
A multi-platform tag points to a list of images, one for each platform. The list has its own digest, and so does each image in it. That's why a registry's page can show a different digest for linux/amd64 than docker buildx imagetools inspect shows for the tag.
Deploy the list's digest, and each machine pulls the image for its platform.
A rebuild is a new image. Building the same code twice usually gives a new digest, since timestamps and installed packages differ. That's the reason to build once and deploy that image.
Deploy by digest
Anything that takes an image reference takes a digest:
containers:
- name: api
image: registry.example/api@sha256:4f5a0123456789abcdef0123456789abcdef0123456789abcdef0123456789abTo keep it readable, put both. Kubernetes uses only the digest for pulling, so the tag is a label for people:
image: registry.example/api:1.4.2@sha256:4f5a0123456789abcdef0123456789abcdef0123456789abcdef0123456789abPin base images
A FROM line can pin a digest too, so a rebuild starts from the same base:
FROM node:22@sha256:4f5a0123456789abcdef0123456789abcdef0123456789abcdef0123456789abA pinned base stops getting security updates until you change the pin, so let Renovate or Dependabot propose new digests.
Digests and promotion
A digest is what makes promotion provable. Staging tests sha256:4f5a…, and production deploys sha256:4f5a…, so production runs the bytes staging tested. A tag can't give you that, because it can move between the two.
Promote a Docker image between environments covers copying an image by digest. hushrun's promotion records signed evidence of what passed, then copies the image staging built to production by digest, with no rebuild.