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.
| Date | What happened or happens | Who notices |
|---|---|---|
| 29 July 2025 | Docker 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 2025 | Docker 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 2026 | Docker publishes the retirement and migration guidance with exact dates. | Everyone still on DCT |
| 14 and 15 July 2026 | Four-hour write brownouts: signing fails, ordinary push still works. | Publishers who sign with docker trust sign |
| 10 and 12 August 2026 | Four-hour read brownouts: trust lookups fail, ordinary pull still works. | Consumers who verify with DCT |
| 8 December 2026 | Full 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
Drag to exploreNotice 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.
| Reference | Example | Can it change later? | What it proves |
|---|---|---|---|
| Tag | payments-api:2.4.1 | Yes, anyone with push rights can move it | Only the name you asked for |
| Digest | payments-api@sha256:9a4b...e316 | No, new bytes give a new digest | You got exactly these bytes |
| Signature with signer identity | Cosign or Notation signature on that digest | A new signature can be added, but it cannot be faked without the key | A 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.
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:
.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.ioCode 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.
| Docker CLI | DOCKER_CONTENT_TRUST=1 | docker trust | --disable-content-trust |
|---|---|---|---|
| 28 and older (we ran 24.0.7) | Enforced: tag pulls ask the Notary server first, and pushes sign through it | Works until the server goes | Skips the check for one command |
| 29 and newer (we ran 29.9.2) | Ignored: the binary does not contain the variable name | unknown command: docker trust, unless the separate plugin is installed | Warning 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?
| Trust server behaviour | Pull by tag | Pull by digest |
|---|---|---|
| Nothing listening on the port | Error: error contacting notary server: connection refused (exit code 1) | Skips Notary and goes to the registry |
| Answers 404 Not Found | Error: remote trust data does not exist for docker.io/library/busybox (exit code 1) | Not tested separately |
| Answers 503 Service Unavailable | unable to reach trust server at this time: 503 (exit code 1) | Not tested separately |
| Same pull on Docker CLI 29.9.2 | Variable ignored, goes straight to the registry | Goes 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.
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.
Pin every image by digest
Replace tags with name@sha256:DIGEST in Dockerfiles, Compose files and Kubernetes manifests. This makes every pull repeatable.
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.
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.
Verify before deploy
Enforce signatures at the cluster door with Kyverno for Cosign, or Ratify with Gatekeeper for Notation. Unsigned images are then refused.
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.
| Question | Sigstore Cosign | Notation (Notary Project) |
|---|---|---|
| Who is the signer? | An OpenID Connect (OIDC) login identity, such as a GitHub Actions workflow, or a key pair | An 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 certificate | Yes, the private key behind your certificate |
| Public record | Keyless signing is logged in Rekor, a public transparency log | Not part of its quickstart; trust comes from your certificate chain |
| Verify on a laptop | cosign verify with --certificate-identity and --certificate-oidc-issuer | notation verify against a trust policy file |
| Enforce in Kubernetes | Kyverno verifyImages rule | Ratify with OPA Gatekeeper, a Kubernetes policy engine |
The replacement flow: sign the digest in CI, check it at deploy
Drag to exploreThe 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.
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.comThe 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.
IMAGE=localhost:5001/net-monitor@sha256:073b75987e95b89f187a89809f08a32033972bb63cda279db8a9ca16b7ff555a
notation sign $IMAGE
notation ls $IMAGE
notation policy import ./trustpolicy.json
notation verify $IMAGEThe 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.
Name the threat
A tag can be moved, and a registry account can be misused. Either can put the wrong image into production.
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.
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.
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.
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
- Docker blog: Docker Content Trust: Retirement and Migration Guidance (16 June 2026)
- Docker docs: Content trust in Docker (retirement notice)
- Docker blog: Retiring Docker Content Trust (29 July 2025)
- Moby release notes: docker-v29.0.0 (DCT removed from the Docker CLI)
- Docker CLI reference: docker trust sign
- Sigstore: Quickstart with Cosign
- Sigstore: CI Quickstart (GitHub Actions signing and verify flags)
- Notary Project: Quickstart, sign and verify an image with Notation
- Kyverno policy: Verify Image
- Ratify: Quick start with Gatekeeper
Related guides
- Docker Containers Explained: Namespaces, Cgroups and the Root FilesystemBackend · 22 min read
- Kubernetes Architecture Explained: Control Plane, Nodes and PodsBackend · 26 min read
- Axios Supply Chain Attack 2026: Malicious npm Versions, Postinstall Malware, and How to Protect Your SystemsBackend · 16 min read