# Docker image digest vs tag

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.4.2` | `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](https://kubernetes.io/docs/concepts/containers/images/) 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:

```bash
docker buildx imagetools inspect nginx:1.27
crane digest nginx:1.27
```

For an image you've pulled:

```bash
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:

```yaml title=".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](https://docs.docker.com/engine/storage/containerd/), 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](https://docs.docker.com/dhi/explore/security-concepts/digests/), 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](https://hushrun.com/docs/guides/build-once-deploy-many.md).

## Deploy by digest

Anything that takes an image reference takes a digest:

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

To keep it readable, put both. Kubernetes [uses only the digest](https://kubernetes.io/docs/concepts/containers/images/) for pulling, so the tag is a label for people:

```yaml title="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:

```dockerfile
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](https://hushrun.com/docs/guides/promote-docker-image.md) covers copying an image by digest. [hushrun's promotion](https://hushrun.com/docs/promotion.md) records signed evidence of what passed, then copies the image staging built to production by digest, with no rebuild.

## Related

- [Promote a Docker image between environments](https://hushrun.com/docs/guides/promote-docker-image.md)
- [Build once, deploy many](https://hushrun.com/docs/guides/build-once-deploy-many.md)
- [Promotion](https://hushrun.com/docs/promotion.md)
