skip to content

What does setting the environment variable `DOCKER_CONTENT_TRUST=1` actually change about `docker push` and `docker pull`, how does the key hierarchy behind it work, and why have most teams moved to something else?

level: middleimportance: nice to knowfreq 26%

answer

  1. DOCKER_CONTENT_TRUST=1 → sign on push, refuse unsigned on pull
  2. TUF roles: root (offline) / targets / snapshot / timestamp
  3. timestamp key defeats freeze + rollback
  4. docker trust inspect --pretty, targets/releases delegation
  5. legacy: tag-scoped, Docker-CLI-only, no transparency log

basics

~20 s

It turns on Docker Content Trust: pushes get signed via a Notary server and pulls or FROM references refuse unsigned or unverified tags. It uses TUF roles — an offline root key, a per-repository targets key, plus server-held snapshot and timestamp keys. Teams moved on because it is tag-scoped, Docker-client-only and painful to operate.

solid answer

~50 s

`DOCKER_CONTENT_TRUST=1` enables **Docker Content Trust (DCT)**. With it set, `docker push` also signs the tag through a **Notary v1** server paired with the registry, and `docker pull` / `docker build`'s `FROM` will only accept tags with a valid signature — an unsigned tag fails rather than silently proceeding. Under the hood it is **TUF (The Update Framework)**: an **offline root key**, a per-repository **targets (repository) key** used for day-to-day signing, and server-managed **snapshot** and **timestamp** keys that provide freshness and prevent rollback/freeze attacks. Delegations (`targets/releases`) let multiple signers publish. Why it faded: signatures are attached to **tags**, not arbitrary artifacts; only the Docker CLI honours it — containerd, Kubernetes and CI tools ignore the variable entirely; key loss is unrecoverable and rotation is awkward; there is no transparency log; and it requires a Notary deployment. Docker deprecated it, and the ecosystem moved to **cosign/Sigstore** and the Notary Project's `notation`, which store detached signatures as OCI artifacts.

code

bash · 5 lines
bash
export DOCKER_CONTENT_TRUST=1
docker push registry.example.com/acme/api:1.4   # signs the tag via Notary

docker trust inspect --pretty registry.example.com/acme/api:1.4
docker trust signer add --key alice.pub alice registry.example.com/acme/api

go deeper

for a junior

Know the variable exists, that it makes pushes signed and unsigned pulls fail, and that modern projects use cosign instead.

for a middle

Explain the TUF roles and what each protects against, and name the concrete limitations that made DCT legacy.

for a senior

Discuss operational reality: key custody and rotation, delegations for multiple signers, and why enforcement must live at admission rather than in a client environment variable.

for a principal

Position it in a migration story — what a move from DCT to cosign/notation costs, how trust roots and identities are re-established, and what enforcement guarantees you actually gain.

## What the switch does Docker Content Trust is opt-in per client. Export `DOCKER_CONTENT_TRUST=1` (or pass `--disable-content-trust=false`) and two behaviours change: - **`docker push`** signs the pushed tag: the digest is recorded and signed into TUF metadata held by a **Notary** server that sits alongside the registry, and the client prompts for/creates keys on first use. - **`docker pull`, `docker run`, and `FROM` in a build** refuse to use a tag that has no valid trust data. Instead of trusting the registry's tag→digest mapping, the client asks Notary for the signed mapping and pulls **that** digest. That last point is the real value: DCT converts a mutable tag lookup into a signed, freshness-checked assertion of which digest the tag means. ## The TUF key hierarchy TUF exists to survive both key compromise and stale-metadata attacks. DCT uses four roles: - **Root key** — the anchor of trust for a repository. Generated on first signed push, meant to be kept **offline** (backed up outside the machine). Losing it means the repository's trust data cannot be recovered. - **Targets (repository) key** — signs the actual tag→digest entries. Used routinely, held by whoever publishes. - **Snapshot key** — signs a consistent view of the metadata set; can be delegated to the Notary server. - **Timestamp key** — held by the server, re-signed frequently and short-lived, so a client can detect **freeze attacks** (an attacker replaying old metadata to hide an update) and **rollback** to a previously good-but-now-vulnerable version. **Delegations** — typically `targets/releases` — let a team add multiple signers without sharing the repository key, and `docker trust signer add` manages them. `docker trust inspect --pretty <image>` shows signers and signed tags. ## Why the ecosystem moved on 1. **Tag-scoped, Docker-only.** Trust data covers tags in the Docker client's world view. Containerd, CRI-O, Kubernetes, buildkit-driven CI and every non-Docker tool ignore `DOCKER_CONTENT_TRUST` completely — so 'we enabled DCT' rarely meant production verified anything. 2. **Operational fragility.** Key generation on first push surprises people; passphrases end up in CI secrets; root key loss is terminal; there is no organisation-wide identity model, just per-repo keys. 3. **No transparency.** Nothing records signing events publicly or internally, so you cannot detect a signature made with a stolen key after the fact. 4. **Infrastructure requirement.** You need a Notary server bound to the registry; hosted support dwindled, and Docker announced deprecation of Content Trust/Notary v1 on Docker Hub. 5. **Only one kind of statement.** Modern supply chains want SBOMs, build provenance and scan attestations attached to an artifact, not just 'this tag is signed'. ## What replaced it Two successors, both storing signatures as **OCI artifacts in the registry itself** (no separate Notary service): - **cosign / Sigstore** — dominant in practice; supports key pairs, KMS keys and 'keyless' short-lived certificates tied to an OIDC workflow identity, with a public transparency log. - **Notary Project `notation`** — the successor line to Notary v1, X.509/PKI-oriented, appealing to organisations with existing certificate authorities. Both sign **digests**, not tags, so signatures survive retagging and mirroring, and both are verified by admission controllers that Kubernetes actually runs. ## What to say in an interview Know DCT exists, know it is TUF-based with root/targets/snapshot/timestamp roles, know the variable and `docker trust inspect`, and be able to explain crisply why it is legacy: tag-scoped, client-scoped, key-fragile, no transparency, superseded by cosign and notation. Recognising legacy tooling *and* the reason it lost is the whole point of this question.

  • What attack does the short-lived timestamp key defend against?
    Freeze and rollback attacks. Without a freshness signal, an attacker who controls the distribution path can keep serving old, validly signed metadata so clients never see a security update, or serve a previously signed vulnerable version. The timestamp role is re-signed frequently and expires quickly, so clients detect stale metadata and refuse it.
  • A team sets `DOCKER_CONTENT_TRUST=1` in CI and says production images are now verified. What is wrong with that claim?
    The variable only affects the Docker CLI on that machine. Kubernetes nodes pull through containerd or CRI-O, which have no knowledge of Content Trust, so nothing is verified at deploy time. Real enforcement needs verification where workloads are admitted — an admission controller checking cosign or notation signatures — not an environment variable on a build agent.

saying these in an interview costs you the question

  • Believing `DOCKER_CONTENT_TRUST=1` makes a Kubernetes cluster verify images.
  • Thinking DCT signs image digests as arbitrary artifacts rather than tags in Notary metadata.
  • Not knowing the root key is meant to be offline, or that losing it is unrecoverable.
  • Presenting DCT as current best practice instead of deprecated legacy.
  • Confusing Notary v1 (DCT) with the Notary Project's `notation`, which stores OCI signatures.

context