Skip to content

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.

TagDigest
Looks likeapi:1.4.2api@sha256:4f5a…
Can changeYes: whoever can push can move itNo: other content has another digest
ReadableYesNo
Use it forVersions people talk aboutDeploys, 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.27

For an image you've pulled:

docker inspect --format='{{index .RepoDigests 0}}' nginx:1.27

When 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:

.github/workflows/build.yml
- 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:

deployment.yaml
containers:
  - name: api
    image: registry.example/api@sha256:4f5a0123456789abcdef0123456789abcdef0123456789abcdef0123456789ab

To keep it readable, put both. Kubernetes uses only the digest for pulling, so the tag is a label for people:

deployment.yaml
image: registry.example/api:1.4.2@sha256:4f5a0123456789abcdef0123456789abcdef0123456789abcdef0123456789ab

Pin base images

A FROM line can pin a digest too, so a rebuild starts from the same base:

FROM node:22@sha256:4f5a0123456789abcdef0123456789abcdef0123456789abcdef0123456789ab

A 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.

Markdown