skip to content

Manifests, Digests & Content Addressing

An image is a manifest pointing at a config blob and layer blobs, each named by its sha256 digest, with an index on top for multi-arch. Interviewers use it to separate image ID from repo digest from tag, and to ask why a rebuild changes the digest.

part ofDockeroverview, primer and where to startread it →
on this pageshow

questions

5

In a container image reference, what is the difference between the tag form nginx:1.27 and the digest form nginx@sha256:1a2b3c..., and what guarantee does each one give you?

level: juniorimportance: must knowfreq 62%

answer

  1. tag = mutable pointer
  2. digest = sha256 of manifest bytes
  3. Merkle tree: manifest covers layers
  4. digest is global, tag is registry state
  5. pin deploys, tag for humans

basics

~20 s

A tag is a mutable label the registry can repoint at new content at any time. A digest is the sha256 hash of the image manifest, so one digest always resolves to byte-identical content. Tags name an image; digests identify it.

solid answer

~50 s

A reference is `registry/repository` plus either `:tag` or `@sha256:<hex>`. A **tag** is a mutable pointer stored in the registry. Anyone with push rights can move `1.27` — or `latest` — onto different content tomorrow, so two hosts pulling the same tag a week apart can legitimately run different bits. A **digest** is the sha256 of the manifest document's exact bytes. Content addressing means the reference *is* the content: change one byte and the digest changes, so a registry cannot serve you different content under the same digest without the client noticing — the client re-hashes what it downloaded before trusting it. Because the manifest lists the config and layer blobs by their own digests, verifying the top hash transitively verifies everything beneath it. Use tags for humans and rolling updates; use digests wherever you need reproducibility or auditability — production rollouts, frozen base images, incident forensics.

code

bash · 5 lines
bash
docker pull nginx:1.27
docker inspect --format '{{index .RepoDigests 0}}' nginx:1.27
# nginx@sha256:9f86d081884c7d659a2feaa0c55ad015...

docker run --rm nginx@sha256:9f86d081884c7d659a2feaa0c55ad015...

go deeper

for a junior

Know the two reference forms, know that tags can move and digests cannot, and be able to show docker inspect reporting a RepoDigest.

for a middle

Explain content addressing as a Merkle tree — the manifest lists layers by digest, so the top hash covers everything — and say where each form belongs.

for a senior

Bring in operational consequences: cached tags diverging across nodes, non-deterministic rollbacks, scan results being meaningless without a digest, registry garbage collection of untagged manifests.

for a principal

Frame it as a supply-chain policy question: which references are allowed at admission, how digest bumps are automated and reviewed, and how integrity from digests is complemented by signatures and provenance.

## Two ways to name a version A reference looks like `registry.example.com/team/api:1.4.2` or `registry.example.com/team/api@sha256:9f86d0…`. Everything before the separator names a *repository* — a namespace inside a registry holding many versions of one image. Everything after selects a version, and there are exactly two mechanisms: a tag or a digest. ## Tags are mutable pointers A tag is a name the registry maps to a manifest. That mapping is ordinary writable registry state: pushing again with the same tag simply repoints it at new content. Nothing in the tag describes what it points to, and nothing prevents it from changing. This is a feature — it is how `nginx:1.27` keeps receiving patch releases and how `latest` tracks a moving head — but it means a tag is not an identity. Consequences you will meet in practice: - Two machines that pulled `app:v3` on different days can be running different binaries, and both are "correct". - A cached tag on a node will not be re-fetched unless the pull policy forces it, so `v3` can mean different things on different nodes simultaneously. - Rolling back "to the tag that worked" is not deterministic if the tag moved. ## Digests are content addresses When you push an image, the registry stores a **manifest**: a small JSON document that lists the image's config blob and its layer blobs, each by size, media type and sha256 digest. The image's digest is the sha256 of that manifest's serialized bytes — not of the tarball, not of the running filesystem, of the manifest document exactly as stored. That makes the whole structure a Merkle tree. The manifest digest covers the manifest bytes; the manifest bytes cover the layer digests; each layer digest covers that layer's bytes. Verifying the top hash therefore transitively verifies every byte you pulled. A registry — or a proxy, or a tampered mirror — cannot substitute different content under an unchanged digest, because the substituted bytes would hash differently and the client's verification would fail. A digest is also *global*. `sha256:9f86d0…` denotes the same manifest in Docker Hub, in your internal registry, and in an air-gapped copy, because the name is derived from the content rather than assigned by a registry. Only the repository prefix in front of it is registry-specific. ## Where each belongs Use tags where mutability is the point: developer convenience, semantic version streams, `latest` for casual use, and any flow where "give me the newest patch" is genuinely what you want. Use digests where identity matters: - Deployment manifests, so a rollout is reproducible and a rollback is exact. - `FROM` lines that must not drift under you between builds. - Provenance and vulnerability records — a scan result is only meaningful against a digest, since "we scanned v3" is meaningless if v3 moved. - Incident forensics: the digest tells you exactly which bits ran. The cost is that digests are opaque and do not update themselves. Pinning by digest means you have taken responsibility for pulling in security patches deliberately, usually with automation that bumps the pinned digest and opens a change for review. ## Getting and using digests `docker pull nginx:1.27` prints the resolved digest, and `docker inspect --format '{{index .RepoDigests 0}}' nginx:1.27` reports it afterwards. Registries expose it as the `Docker-Content-Digest` response header on a manifest request. You can pull, run and reference by digest anywhere a tag is accepted, and you may combine them — `nginx:1.27@sha256:…` — where the tag is documentation for humans and the digest is what actually resolves.

  • If a tag is mutable, can a digest reference ever break?
    The digest cannot start resolving to different content, but it can stop resolving at all. Registries garbage-collect manifests and blobs that are no longer referenced by any tag, and many retention policies delete untagged manifests after a window. So pinning by digest requires that something still keeps that manifest alive — a retained tag, an immutable-tag policy, or excluding pinned digests from cleanup.
  • Does pulling by digest remove the need to verify signatures?
    No. A digest proves integrity — that you got exactly the bytes that digest names — but says nothing about who produced them or whether they were authorised. If an attacker can influence which digest ends up in your manifest, integrity alone does not help. Signing and provenance attestations bind a digest to a publisher and a build; the digest is what they sign over.

A tag is like a bookmark labelled 'current draft' — someone can point it at a new document. A digest is like a fingerprint of the document itself: it can only ever mean that exact document.

saying these in an interview costs you the question

  • Saying the digest is a hash of the image tarball or of the running container filesystem, rather than of the manifest document
  • Believing a tag is immutable because it looks like a version number, so `:1.27` must always be the same image
  • Claiming that pulling by digest guarantees the image is safe or trusted, confusing integrity with authenticity
  • Assuming re-pulling a tag always fetches new content, ignoring that a locally cached tag is used as-is unless the pull policy forces a check

context

open as a page

How can one reference such as alpine:3.20 work on both linux/amd64 and linux/arm64 machines — what does the registry return, and how is the correct variant selected?

level: middleimportance: must knowfreq 50%

basics

~20 s

The tag points at a manifest list (OCI image index): a JSON document listing one manifest digest per platform, each annotated with os/architecture/variant. The client picks the entry matching its own platform and pulls only that image's config and layers.

open as a page

`docker images` shows an IMAGE ID starting with sha256, and `docker inspect` also reports a RepoDigests entry like repo@sha256:.... Why are those two sha256 values different, and which one identifies what you pulled from the registry?

level: middleimportance: should knowfreq 44%

basics

~20 s

The image ID is the sha256 of the image's local config JSON blob. The repo digest is the sha256 of the manifest the registry served. The manifest points at the config plus layers, so the two hash different documents. Registry references use the repo digest.

open as a page

Walk through what a registry stores for a single-platform container image: which fields in the manifest identify the layers, and why does the config blob's rootfs.diff_ids list different sha256 values than the manifest's layer descriptors?

level: seniorimportance: should knowfreq 34%

basics

~20 s

The manifest lists a config descriptor and layer descriptors — digests of the compressed blobs as transferred. The config blob lists rootfs.diff_ids — digests of the same layers uncompressed. Different bytes hashed, so different values; diff_ids identify layers on disk, layer digests identify them on the wire.

open as a page

Your deployment manifests reference every container image by tag. A colleague proposes pinning all of them to sha256 digests instead. How would you decide, and what machinery does digest pinning require to be sustainable?

level: principalimportance: should knowfreq 38%

basics

~20 s

Pinning buys reproducible, auditable, exactly rollback-able deployments and closes tag-mutation risk. It costs you automatic patching, so it only works with automated digest bumps, registry retention that never garbage-collects pinned manifests, and a clear rule about which references may still be tags.

open as a page