SevyDevy

Guide · Backend

Docker Content Trust Shutdown (December 2026) Explained: Digests and Signers

19 min readUpdated By Saket Bhatnagar

Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.

In short

  • The Docker Content Trust shutdown means Docker turns off notary.docker.io, the server that stored image tag signatures, on 8 December 2026. Its brownouts, short planned outages that warn users, already ran in July and August.
  • Docker Content Trust (DCT) is Docker's old opt-in check that a tag, the readable name like api:2.4, was signed. You switch it on with DOCKER_CONTENT_TRUST=1.
  • The replacement has two parts. A digest, the sha256 fingerprint of the image, fixes the exact bytes. A signature from Cosign or Notation, two current signing tools, proves who published them.
  • First, search your pipelines for DOCKER_CONTENT_TRUST and docker trust, and check your Docker CLI version. Docker CLI 29 removed DCT, so the variable now does nothing there.
  • Beginners can stop after the migration steps. Engineers then get our test of what an old Docker CLI does when the trust server is gone, and why digest pulls skipped it.

What goes wrong when Docker Content Trust shuts down on 8 December 2026?

Any script that still asks Docker's old signing server about an image tag will get no answer, and a pull that needs that answer fails. The Docker Content Trust shutdown removes that server, notary.docker.io, on 8 December 2026. As of 28 October 2026, that is 41 days away.

Most people are not affected. Docker's June 2026 post says fewer than 0.05% of Docker Hub pulls rely on DCT. A plain docker pull never talks to the signing server.

Picture a common setup. A payments team switched on content trust years ago on its build servers, because an audit asked for signed images. The variable sits in a shared shell profile, and nobody remembers it. The setup is made up, but the trap is real. When the server goes, tag pulls on older Docker versions stop with an error about the notary server.

There is a second, quieter problem. Docker CLI 29, released on 10 November 2025, removed content trust from the client. On those machines the same variable is now ignored without a warning. The team believes it checks signatures, but nothing has been checked since the upgrade.

Which words do you need before migrating off Docker Content Trust?

You need six words: image, registry, tag, digest, signature and signer identity. Each gets one plain meaning and one example.

  • Image: a packaged app with everything it needs to run, stored as layers plus a small JSON file called a manifest that lists them. Example: the build of your payments API.
  • Registry: the server that stores images, such as Docker Hub or your company's own. You push images to it and pull them from it.
  • Tag: a readable name that points to one image, like payments-api:2.4.1. The owner can move a tag to a different image at any time.
  • Digest: the sha256 fingerprint of the image manifest, or of the image index for multi-platform images. It is written sha256: followed by 64 hexadecimal characters. Change one byte and the digest changes, so different content gets a different digest.
  • Signature: a small piece of data, made with a private key, that anyone holding the matching public key or certificate can check.
  • Signer identity: who made the signature, such as a named key, a company certificate or a CI workflow. CI, continuous integration, is the system that builds and tests every change. Checking it answers the question "did my team publish this?"

Two more names appear below. Notary v1 is the open-source signing server behind DCT, and notary.docker.io is Docker's hosted Notary v1 service. An OCI artifact is any extra file a registry stores next to an image, such as a signature.

What is the Docker Content Trust shutdown in 2026?

The Docker Content Trust shutdown is the end of Docker's Notary v1 service, which stored the signed records that DCT checks. Docker's docs now open with two plain sentences. DCT "is being retired", and notary.docker.io "will shut down on December 8, 2026".

Docker gives three reasons in its June post. The Notary v1 code is no longer maintained upstream. The industry has moved to signing tools that store signatures in the registry itself. Azure Container Registry and Harbor had already deprecated their Notary v1 support.

Docker Content Trust retirement timeline (as of 28 October 2026)
DateWhat happened or happensWho notices
29 July 2025Docker announces the retirement. Old signing certificates for Docker Official Images start expiring from 8 August 2025.Anyone pulling official images with DOCKER_CONTENT_TRUST=1
10 November 2025Docker 29.0.0 removes DCT from the Docker CLI, so the variable stops working. The docker trust commands survive only as an optional plugin that still uses notary.docker.io.Teams that upgraded the CLI, often silently
16 June 2026Docker publishes the retirement and migration guidance with exact dates.Everyone still on DCT
14 and 15 July 2026Four-hour write brownouts: signing fails, ordinary push still works.Publishers who sign with docker trust sign
10 and 12 August 2026Four-hour read brownouts: trust lookups fail, ordinary pull still works.Consumers who verify with DCT
8 December 2026Full shutdown of notary.docker.io.Every remaining DCT user, permanently

A brownout is a planned short outage that warns users before the real one. Docker ran writes first, so publishers who sign would notice before the people who verify. If a DCT pipeline failed for about four hours on one of those August evenings in India, this is why.

How did Docker Content Trust work?

DCT signed the link between a tag and a digest, and kept that signed link on a separate Notary server. When you switched it on, the Docker CLI asked Notary which digest a tag should mean, then pulled that digest from the registry. Our test trace later in this guide shows that order: trust data first, registry second.

Docker's command reference shows this shape. The command docker trust sign example/trust-demo:v2 "adds signatures to tags". Then docker trust inspect lists each signed tag, its digest and its signer.

A tag pull with DOCKER_CONTENT_TRUST=1 on an older Docker CLI

Notice that the CLI talks to the Notary server before the registry. Remove that server and a tag pull stops at the second box.

Diagram connections:

  • Docker CLI 28 or older to notary.docker.io (1. What does 2.4.1 mean?)
  • notary.docker.io to Signature valid? (2. Signed tag record)
  • Signature valid? to Registry (Yes: pull digest X)
  • Signature valid? to Pull refused (No, or no answer)
  • Docker CLI 28 or older: docker pull payments-api:2.4.1 with DCT=1
  • notary.docker.io: Signed record: tag 2.4.1 means digest X
  • Signature valid?: Checked with the repository signing keys
  • Registry: Docker CLI asks for the image by digest X
  • Pull refused: Error before any image bytes download

An everyday picture helps. A tag is the label on a shop shelf, and the shopkeeper can swap the box behind it. A digest is the barcode printed on the box itself. A signature is a sealed delivery note from a supplier you know.

DCT checked a signed note that said which barcode belongs behind each label. Where the picture breaks: a printed barcode can be stuck onto a fake box. A digest is computed from the bytes themselves, so fake bytes always show a different digest.

What is the difference between a tag, a digest and a signer identity?

A tag tells you a name, a digest tells you the exact content, and a signer identity tells you who vouched for that content. You need the last two together to replace DCT.

We checked the first point on Docker Engine 29.9.2. We imported a tiny image as demo/app:1.0, then imported different content under the same tag. The image ID moved, from sha256:a7147cad... to sha256:72e177f1..., and nothing warned us.

Three ways to name an image, and what each one proves
ReferenceExampleCan it change later?What it proves
Tagpayments-api:2.4.1Yes, anyone with push rights can move itOnly the name you asked for
Digestpayments-api@sha256:9a4b...e316No, new bytes give a new digestYou got exactly these bytes
Signature with signer identityCosign or Notation signature on that digestA new signature can be added, but it cannot be faked without the keyA trusted person or workflow published these bytes

Docker's June post puts the gap in one line. Digest pinning "verifies content integrity", but "does not by itself prove publisher identity". A digest copied from an attacker's image is still a perfectly valid digest.

How do you find Docker Content Trust in your pipelines?

Search your code and CI settings for the variable and the trust commands, then check which Docker CLI each machine runs. Docker's post lists the usual hiding places: shell profiles, CI configs, Dockerfiles, Compose files and Kubernetes manifests. Kubernetes manifests are the files that tell a cluster which images to run.

bash
grep -rnE "DOCKER_CONTENT_TRUST|docker trust|disable-content-trust|notary\.docker\.io" .

We ran it on a small test repository with DCT planted in four places. It found all of them, including the hidden .github folder:

text
.github/workflows/release.yml:2:  DOCKER_CONTENT_TRUST: "1"
.github/workflows/release.yml:6:      - run: docker trust sign registry.example.com/shop/api:2.4.1
Dockerfile:2:ENV DOCKER_CONTENT_TRUST=1
ci_env.sh:1:export DOCKER_CONTENT_TRUST=1
ci_env.sh:2:export DOCKER_CONTENT_TRUST_SERVER=https://notary.docker.io

Code search misses settings stored outside the repository. Also check CI variables set in your CI tool's web settings, build-server images, and any Kubernetes admission policy that verifies DCT signatures. An admission policy is a check the cluster runs before it accepts a new workload.

Check the Docker CLI version on each machine

Run docker version --format '{{.Client.Version}}' on every build server and runner. The answer decides what DCT is really doing there.

What DCT does on each Docker CLI (tested 28 October 2026)
Docker CLIDOCKER_CONTENT_TRUST=1docker trust--disable-content-trust
28 and older (we ran 24.0.7)Enforced: tag pulls ask the Notary server first, and pushes sign through itWorks until the server goesSkips the check for one command
29 and newer (we ran 29.9.2)Ignored: the binary does not contain the variable nameunknown command: docker trust, unless the separate plugin is installedWarning on pull and run; docker build with Buildx rejects it, as on older CLIs

The build cell needs care. docker build hands the work to Buildx, Docker's build engine, and Buildx has no such flag. On 29.9.2 with Buildx 0.38.1 it failed with "unknown flag: --disable-content-trust" and exit code 125. Our 24.0.7 CLI with the same Buildx failed the same way, so this is not new in CLI 29. Only the legacy builder accepts the flag, and CLI 29 just warns.

What happens to a pull with DOCKER_CONTENT_TRUST=1 after 8 December 2026?

Docker's June 2026 post does not say it in words; its table only reads "Full shutdown". Our tests show what an older CLI does when it cannot get trust data. It refuses tag pulls.

Docker's July 2025 post was blunter about official images. It warned that with DOCKER_CONTENT_TRUST=1, official image "pulls will start to fail" as old certificates expired. Its fix was to unset the variable.

What we ran

We could not reach notary.docker.io from our test machine. So we pointed Docker CLI 24.0.7 at local stand-in servers with the DOCKER_CONTENT_TRUST_SERVER variable. Each one played a different kind of gone server. Pause and predict: what happens to a pull by digest when the trust server is gone?

Docker CLI 24.0.7 with DOCKER_CONTENT_TRUST=1 against a missing trust server
Trust server behaviourPull by tagPull by digest
Nothing listening on the portError: error contacting notary server: connection refused (exit code 1)Skips Notary and goes to the registry
Answers 404 Not FoundError: remote trust data does not exist for docker.io/library/busybox (exit code 1)Not tested separately
Answers 503 Service Unavailableunable to reach trust server at this time: 503 (exit code 1)Not tested separately
Same pull on Docker CLI 29.9.2Variable ignored, goes straight to the registryGoes straight to the registry

Our stand-in server logged two requests from the CLI: GET /v2/ and then the repository's root.json, the top trust file. What just happened? The CLI asked for trust data first, got none, and stopped. The refused pulls never reached the registry at all.

Why the digest pull skipped Notary

DCT exists to turn a tag into a trusted digest. When you already give a digest, there is no tag left to look up, so the older CLI went straight to the registry. That is why pinning digests is the safe first step even before you remove the variable.

How do you migrate from Docker Content Trust step by step?

Remove the dependency first, then rebuild the two guarantees separately: digests for exact content and new signatures for publisher identity. This follows the order in Docker's June 2026 guidance.

  1. Unblock before 8 December

    Remove DOCKER_CONTENT_TRUST=1 everywhere the search found it, or set it to 0. Delete docker trust sign, inspect and revoke steps from scripts. You lose tag checks, so do the next steps soon.

  2. Pin every image by digest

    Replace tags with name@sha256:DIGEST in Dockerfiles, Compose files and Kubernetes manifests. This makes every pull repeatable.

  3. Pick a signer

    Choose Sigstore Cosign or the Notary Project's Notation, both maintained today. Docker cannot sign for you; publishers must adopt one themselves.

  4. Sign in CI, by digest

    Add a signing step right after the image push, using the digest the push printed. Signatures are stored in the registry next to the image.

  5. Verify before deploy

    Enforce signatures at the cluster door with Kyverno for Cosign, or Ratify with Gatekeeper for Notation. Unsigned images are then refused.

  6. Ask your upstream publishers

    If you consumed third-party images signed with DCT, ask each publisher how they sign now. Docker's post suggests Docker Hardened Images for base images, which ship with signatures built in.

Kubernetes deserves a word here. An admission controller is the program that runs those admission checks before the cluster accepts a new pod. Kyverno's sample policy can also rewrite tags to digests with mutateDigest: true.

Should you replace Docker Content Trust with Cosign or Notation?

Pick Cosign if you want to sign from CI with no long-lived keys. Pick Notation if your company already runs certificates and a certificate authority. Both store signatures as OCI artifacts in the same registry as the image.

Cosign vs Notation as DCT replacements (from their official quickstarts, checked 28 October 2026)
QuestionSigstore CosignNotation (Notary Project)
Who is the signer?An OpenID Connect (OIDC) login identity, such as a GitHub Actions workflow, or a key pairAn X.509 certificate, the standard digital certificate format, from your certificate authority
Long-lived private key?Not in keyless mode: Fulcio, Sigstore's certificate service, issues a short-lived certificateYes, the private key behind your certificate
Public recordKeyless signing is logged in Rekor, a public transparency logNot part of its quickstart; trust comes from your certificate chain
Verify on a laptopcosign verify with --certificate-identity and --certificate-oidc-issuernotation verify against a trust policy file
Enforce in KubernetesKyverno verifyImages ruleRatify with OPA Gatekeeper, a Kubernetes policy engine

The replacement flow: sign the digest in CI, check it at deploy

The signature now lives in the registry next to the image, and the cluster checks both the digest and who signed it. No separate trust server is involved.

Diagram connections:

  • CI job to Cosign or Notation (1. Push, then sign digest)
  • Cosign or Notation to Registry (2. Upload signature)
  • Registry to Kyverno or Ratify (3. Fetch signature on deploy)
  • Kyverno or Ratify to Trusted signer?
  • Trusted signer? to Pod runs (Yes)
  • Trusted signer? to Pod refused (No)
  • CI job: Builds and pushes payments-api, gets its digest
  • Cosign or Notation: Signs payments-api@sha256:digest
  • Registry: Stores image plus signature as OCI artifact
  • Kyverno or Ratify: Admission check before a pod is accepted
  • Trusted signer?: Identity and issuer, or certificate, match policy
  • Pod runs: Pulled by digest, so bytes cannot change
  • Pod refused: Unsigned image or wrong signer

Signing and verifying with Cosign in GitHub Actions

The Sigstore CI quickstart gives the job permission id-token: write, so it can ask GitHub for an identity token. It then signs each image reference, kept in a shell variable called images, in the form tag@digest. These commands come from that page; we could not run Cosign in our test setup.

bash
cosign sign --yes ${images}

cosign verify ${images} \
  --certificate-identity=https://github.com/USERNAME/REPOSITORY_NAME/.github/workflows/WORKFLOW_NAME@refs/heads/BRANCH_NAME \
  --certificate-oidc-issuer=https://token.actions.githubusercontent.com

The two flags are the signer identity. They say which workflow, on which branch, through which issuer, may have signed. A signature from any other workflow fails the check.

Signing and verifying with Notation

The Notation quickstart signs a digest reference, then verifies it against a trust policy. The policy names which registries it covers, which certificate store to trust and which identities to accept. These commands also come from that page, not from our runs.

bash
IMAGE=localhost:5001/net-monitor@sha256:073b75987e95b89f187a89809f08a32033972bb63cda279db8a9ca16b7ff555a
notation sign $IMAGE
notation ls $IMAGE
notation policy import ./trustpolicy.json
notation verify $IMAGE

The quickstart's notation cert generate-test command makes a self-signed test certificate. Its page says that is for testing only, so use a real certificate authority in production.

How do you pin Docker images by digest?

Write the image as name@sha256:DIGEST wherever you write a tag today. Docker's post shows the pull form, docker pull busybox@sha256:DIGEST, and asks for the same in Dockerfiles, Kubernetes manifests and Compose files.

To find a digest, pull or push the image and read it back. The Notation quickstart uses docker inspect --format='{{index .RepoDigests 0}}' IMAGE. On our 29.9.2 test it printed the repository name, an @ sign and the digest.

Pinning has a cost: you no longer get fixes by pulling the same tag again. Treat each digest change like a dependency update, reviewed in a pull request. The Notation page says why in one line: "Always reference and use the image digest instead of a tag since digest is immutable."

How do you check that the migration worked?

Check three things: no DCT left, every image pinned, and every deploy verified. Each check has a command and an expected result.

  • No DCT left: the search command from earlier prints nothing and exits with code 1, which is how grep reports no matches.
  • Digests are well formed: each one is sha256: followed by exactly 64 hexadecimal characters, which is 32 bytes. Our one-byte change test gave two completely different digests.
  • Cosign: cosign verify with your real identity and issuer must succeed. Run it once more with a wrong identity, which must fail.
  • Notation: notation verify returns the digest of the image when verification succeeds, according to its quickstart.
  • Cluster: deploy one unsigned test image and confirm it is refused. Ratify's quickstart shows the message "Subject failed verification" for its unsigned sample.

How do you explain the Docker Content Trust shutdown in a system design interview?

Frame it as a supply-chain design question: how does a cluster know an image is exact and came from you? Then walk through the answer in order.

  1. Name the threat

    A tag can be moved, and a registry account can be misused. Either can put the wrong image into production.

  2. Separate two guarantees

    A digest proves exact content. A signature with a checked identity proves who published it. DCT tied both to tags through a separate server.

  3. Explain why DCT went away

    Notary v1 was unmaintained, and notary.docker.io shuts down on 8 December 2026. Docker CLI 29 had already removed DCT.

  4. Draw the new flow

    CI pushes, signs the digest with Cosign or Notation, and stores the signature in the registry. An admission controller checks it before any pod runs.

  5. Cover failure and upkeep

    Plan for key or certificate rotation, a negative test with an unsigned image, and digest updates through reviewed pull requests.

If the interviewer pushes further, mention that keyless signing writes to a public transparency log. That lets you audit every signature made in your workflow's name.

Questions people ask

Will a normal docker pull stop working on 8 December 2026?
No. A normal pull never contacts notary.docker.io, and Docker's post said ordinary pull and push would keep working during the brownouts. Only commands that use Docker Content Trust depend on the server. On Docker CLI 28 or older with DOCKER_CONTENT_TRUST=1, that means tag-based pulls and runs, pushes, which sign, and docker trust commands. Our digest pull skipped the server.
Is it safe to just set DOCKER_CONTENT_TRUST=0?
It is safe for availability, but you lose the tag check. Docker's own guidance lists it as the quick unblock, then tells you to pin digests and add Cosign or Notation signatures. Treat the variable change as a temporary step, not the finished migration.
I run Docker CLI 29. Do I need to do anything?
Yes, but for a different reason. Docker 29.0.0 removed DCT from the CLI, so the variable is ignored and nothing is being verified. Remove the dead settings so nobody believes they work. Then add digest pinning and signature checks. Also remove --disable-content-trust from build scripts, because Buildx 0.38.1 rejects it.
Are Docker Official Images still signed after DCT?
Not with DCT. Docker said in July 2025 that the oldest official image DCT certificates would start expiring on 8 August 2025, and that DCT pulls of those images would fail. For signed base images today, Docker points to Docker Hardened Images, which ship with signatures, provenance records and SBOMs, lists of what is inside an image.
Do I need Kubernetes to use Cosign or Notation?
No. Both have command-line verify commands you can run in any CI job before deploying. Kubernetes tools like Kyverno, or Ratify with Gatekeeper, add enforcement at the cluster, so a pod with an unsigned image cannot start even if someone skips the CI step.
Does pinning a digest mean I stop getting security fixes?
Yes, until you update the digest. A pinned image never changes, which is the point. Plan regular digest updates as reviewed pull requests, the same way you update library versions, so fixes arrive on purpose rather than by surprise.

Sources and further reading

Practise this with Lala

Practise