Will my pipeline break when Docker Notary v1 shuts down on December 8, 2026?
Your pipeline is affected if it uses Docker Content Trust: DOCKER_CONTENT_TRUST set to anything other than a false value, docker trust signing commands, or a cluster or deployment policy that checks Content Trust signatures, against Docker Hub. With Content Trust off, ordinary docker pull and docker push do not touch the Notary service. If you have never turned Content Trust on, this change does not apply to you. If you have, find every place that switches it on, then choose between turning verification off and replacing it with signatures that do not depend on Notary v1.
One case needs care: a Docker CLI from version 29 onward ignores DOCKER_CONTENT_TRUST. That client will not fail on December 8 because of that variable, and it checks nothing on pull. Only docker trust steps run through a separately built docker-trust plugin would still fail. A pipeline that still sets the variable is not protected by it.
Jump to: the date · who breaks · check a repository · check a host or runner · fix: turn it off · fix: replace it · what failure looks like · limits.
The date
Checked against Docker's primary sources on November 14, 2026 (UTC):
- December 8, 2026 — Notary v1 shuts down. Docker's Content Trust documentation says: “Docker Content Trust (DCT) is being retired. The Notary v1 service at notary.docker.io will shut down on December 8, 2026.”
- Docker's retired-features page says the service “shuts down completely on December 8, 2026, following brownout windows in July and August,” and “If you've never set DOCKER_CONTENT_TRUST=1 or used docker trust commands, this change doesn't affect you.”
- Docker's retirement and migration post, posted June 16, 2026, lists the brownouts: write brownouts on July 14 and 15, read brownouts on August 10 and 12, and “Full shutdown” on December 8. It says windows “run ~4 hours and begin at 8AM Pacific Time” and that “ordinary docker pull and docker push operations will keep working through these windows.” We did not find a published time of day for the final shutdown.
- The same post says fewer than 0.05% of Docker Hub pulls rely on Content Trust, and that Docker “cannot provide replacement signatures on your behalf” to publishers who sign with it.
On November 14, 2026 at 01:03 UTC, https://notary.docker.io/ still answered with HTTP 401, the normal authentication challenge. The service was therefore still reachable; we could not observe the shutdown itself.
Who breaks
Docker's post lists four signs that you used Content Trust: the variable set in a shell profile, CI pipeline or Dockerfile; docker trust sign, inspect or revoke in scripts; Kubernetes admission controllers or deployment policies that check Content Trust signatures; and publishing to Docker Hub with signing enabled. We tested the first two with real Docker client binaries:
docker trustcommands against Docker Hub (Docker CLI 28 and earlier): Docker's post namessign,inspectandrevoke. They work through the Notary service and stop working with it. We testedinspect; we did not runsignorrevoke. These commands are not built into Docker CLI 29.DOCKER_CONTENT_TRUST=1on Docker CLI 28 or earlier, pulling a Docker Hub image by tag: the client contactsnotary.docker.iobefore it contacts the Docker daemon, and fails if that service cannot answer. Docker CLI source for version 27.5.1 selectshttps://notary.docker.iofor Docker Hub images. For another registry, the client uses that registry's own host instead. In our test it looked upregistry.example.org, so Docker's shutdown does not remove a trust server you run yourself, although that registry must still have one.- Any value that is not false turns it on.
1,trueand alsoyesenable Content Trust. Only0,f,F,false,False,FALSEand an empty value leave it off. We confirmed thatDOCKER_CONTENT_TRUST=contacted the trust server on Docker CLI 27.5.1 and 28.5.2.yes - Pull by digest is not gated. With the variable set to 1,
docker pull busybox@sha256:…went straight to the daemon without contacting the trust server on both of those versions. - Docker CLI 29 and later has no built-in Content Trust. On 29.0.0, 29.1.3 and 29.9.3 the variable never caused a trust-server request,
docker trustanswered “unknown command”, and--disable-content-trustprinted “support for docker content trust was removed.” A pipeline whose client is 29 or later is not verifying signatures on pull. It will pass on December 8 and checks nothing on pull. Onlydocker truststeps run through the separately builtdocker-trustplugin would still fail. Docker's 29.0 release notes say: “Docker Content Trust was removed from the Docker CLI. Can be built as a separate plugin.” In Docker's source for 29.0.0 that plugin (cmd/docker-trust) still has thesign,inspect,revoke,keyandsignercommands, which use the Notary service and would stop working with it. We did not build or run the plugin. Pull and push are not part of it, so they stay ungated on 29 and later either way. Host mode below reports whetherdocker trustexists on the machine it runs on.
Some pipelines may already be broken. Docker's post Retiring Docker Content Trust, posted July 29, 2025, says: “Starting on August 8th, 2025, the oldest of DOI DCT signing certificates will begin to expire.” and “If you have set the DOCKER_CONTENT_TRUST environment variable to True ( DOCKER_CONTENT_TRUST=1 ), DOI pulls will start to fail. The workaround is to unset the DOCKER_CONTENT_TRUST environment variable.” DOI means Docker Official Images such as busybox, nginx and python. So a job that pulls an Official Image with Content Trust on may already fail, and December 8 extends the failure to every Docker Hub repository. Our test pulls used busybox against a stand-in trust server, not Docker's real one, so we did not observe a real Official Image pull fail and do not claim that it has failed for everyone.
We did not test docker build, a populated local trust cache, or any Kubernetes admission controller. Do not assume a cache keeps a pull working after the service is gone.
Check a repository
Save the Docker Content Trust checker in the repository folder, or anywhere else if you give its full path in the command. Then run this in the repository folder:
python3 -I check-docker-content-trust.py .
echo "exit status: $?"It reads text files and prints one path:line: [CATEGORY] message per finding. It reports:
ENABLED:DOCKER_CONTENT_TRUSTset to a non-false value, includingyes, or--disable-content-trust=.false SIGNING:docker trust sign|, a Content Trust key passphrase variable (the value is never printed), or ainspect| revoke| key| signer notaryclient command.SERVER:notary.docker.io,DOCKER_, or aCONTENT_ TRUST_ SERVER trustServerornotaryv1policy setting.UNRESOLVED: the variable is mentioned but its value comes from elsewhere, such as${DCT:-1}. Read that line.DISABLED: set to a false value,unset, or--disable-content-trust. These lines are informational and do not make the exit status 1.
Exit status 0 means nothing was found, 1 means at least one finding, 2 means a bad path. Commented lines are skipped. It skips copies of itself, under any file name, and prints a note: line for each one it skipped, because its own text names the variable it looks for. It skips documentation (.md, .rst, .txt, .html), lock files, binary files, files over 1 MB, and .git, node_modules, vendor and similar directories. It never opens a Docker trust directory in repository mode.
If you will not download a script, this command lists the same words but cannot tell “on” from “off” and cannot skip comments. It leaves out a saved copy of the checker:
grep -rnIE 'DOCKER_CONTENT_TRUST|notary\.docker\.io|docker +([^ ]+ +){0,2}trust +(sign|inspect|revoke|key|signer)' \
--exclude-dir=.git --exclude-dir=node_modules --exclude='check-docker-content-trust*' .Observed on November 14, 2026. We ran the checker (Python 3.14.4) on public source trees. On Notary v0.7.0 it scanned 334 text files and reported 9 findings, including buildscripts/ and buildscripts/. On Docker CLI v27.5.1 it scanned 977 text files and reported 32 findings and 8 informational lines. Those are those projects' own tests of this feature, not examples of broken production pipelines. A first version that scanned only workflow, Dockerfile, shell and config file names missed the Python and Go files; we changed it to scan code as well. We then compared its flagged files with an independent grep of both trees. The two agreed except for two grep-only files, which matched only on a flag definition or on variable names beginning “trustServer”, neither a use of the feature.
We also ran the steps above exactly as written. A repository holding only FROM alpine and a saved copy of the checker, including a copy with a browser-style name such as check-docker-content-trust (1).py, gave 0 findings and exit 0, with a note: line for each copy. An earlier draft of the checker scanned its own copy and reported 16 findings on that same folder; this version does not. On synthetic fixtures it reported 13 findings in a repository that set the variable in a workflow, Dockerfile, compose file, Jenkinsfile and shell script (exit 1), 0 findings in a repository that only sets it to 0 (exit 0), and 0 on a clean repository (exit 0). A plain search for the same words flags the 0 case too, so a pipeline you had already fixed would still look broken. A deliberately weakened copy of the checker that only accepted 1 and true missed the yes line, so that fixture does detect the weakness.
Check a host or runner
python3 -I check-docker-content-trust.py --host
docker version --format '{{.Client.Version}}'Host mode looks at this process's environment, common shell start-up files (~/.bashrc, ~/.profile, /etc/environment, /etc/profile.d/* and similar), and whether ~/.docker/trust exists. It counts files there and never reads them: a directory of signing keys is exactly what you do not want printed. On a synthetic account with the variable exported, one key file and one cached repository, it reported all three and exited 1; with the directory removed and the variable set to 0 it exited 0. It also prints the Docker client version. When that is 29 or later it runs docker trust --help and says whether a docker-trust plugin is present (the command exits 0 either way, so the checker looks for its usage line). We tested the no-plugin answer on the real 29.0.0 and 29.9.3 binaries and the plugin-present answer only on a stand-in docker script, not on a real plugin.
Run it as the account your CI runner uses, on a self-hosted runner or build image. It does not see other users' accounts, running processes, or settings held by your CI provider rather than in files: repository, environment or organization variables, secrets stores, or a hosted runner image's own environment. Check those in your provider's settings, and search your actions, orbs, templates and base images for the variable too.
Fix: turn Content Trust off
This is Docker's quickest path: “If your only goal is to ensure that image pulls keep working past the shutdown date, disable DCT.” It removes tag-level verification, so it is a security decision and not a repair.
Remove the variable or set it to a false value in every place the checker found. In a shell, either remove it for the current session:
unset DOCKER_CONTENT_TRUSTor set it explicitly off:
export DOCKER_CONTENT_TRUST=0In a Dockerfile use ENV DOCKER_CONTENT_TRUST=0, in compose and CI files set the value to 0 or delete the line, and delete docker trust steps that no longer have anything to sign for. Then re-run the checker: it should exit 0 and list the remaining lines as DISABLED.
Observed on November 14, 2026. With a stand-in trust server that answered every request 503, docker pull busybox:latest on Docker CLI 27.5.1 and 28.5.2 failed with the trust error when the variable was 1 or yes. With 0, false, unset, or --disable-content-trust, the same command skipped the trust server and failed only because the test had no Docker daemon, which is the error we want to see there. In the repository test, editing a copy of the failing fixture the way the steps above describe turned the checker's exit status from 1 to 0 and left five informational lines.
To keep some integrity protection without Notary, pull by digest, as Docker's post suggests: docker pull busybox@sha256:…. A digest proves you received the content you named. It does not prove who published it. Digest pinning was not gated by Content Trust in our tests.
Fix: replace it with Cosign
If your goal is to keep proving who published an image, Docker names two signing replacements: Sigstore Cosign and the Notary Project's Notation. We tested a key-based Cosign round trip, and not Notation.
On a publisher, replace docker trust sign with signing the image digest. On a consumer, replace DOCKER_CONTENT_TRUST=1 with an explicit verification step that fails the job:
Replace the placeholder with your image's name and digest first. The quotes keep every shell from reading it as a redirect.
IMAGE='example/app@sha256:REPLACE_WITH_THE_IMAGE_DIGEST'
cosign generate-key-pair
cosign sign --key cosign.key "$IMAGE"
cosign verify --key cosign.pub "$IMAGE"Observed on November 14, 2026. We ran Cosign v3.1.4 (binary checksum matches the project's published release checksum) in a networkless container against a stand-in loopback registry. Signing a tiny image by digest succeeded, and verification with the matching public key passed with exit 0. Three controls failed as they should: verifying before signing (“no signatures found”, exit 10), verifying a different, unsigned image with the same key (exit 10), and verifying the signed image with a different public key (exit 1). Because that registry had no transparency log we also had to add --use-signing-config=false --tlog-upload=false to the sign command and --insecure-ignore-tlog=true to verify. Do not copy those flags to production. Use your normal Cosign setup, with keyless identity signing or a transparency log, and keep the private key out of CI logs.
We did not test keyless signing, a real registry, key storage, Notation, or enforcing signatures at deploy time. Docker's post names Kyverno for Cosign signatures and Ratify with Gatekeeper for Notation. Existing Notary signatures do not become Cosign signatures: publishers must sign again, and consumers must learn the new public key or identity. If you consume images that third parties sign with Content Trust, Docker's advice is to ask those publishers whether they will adopt modern signing.
There is no automatic fix here. Turning verification off and re-establishing it are different decisions, and the second needs your key, registry and deployment policy.
What failure looks like
We could not observe the shutdown because it has not happened. The documented failures are Docker's brownouts: write brownouts on July 14 and 15, which the post says break signing first, and read brownouts on August 10 and 12. We found no public record of the exact text people saw in those windows.
The following is a contained reproduction and not Docker's real response. We pointed DOCKER_ at a loopback HTTPS server on the same machine, with no network access, and ran real Docker CLI binaries without a daemon. A server that refused connections gave Error: error contacting notary server: dial tcp 127.0.0.1:8444: connect: connection refused (exit 1). A server that answered 503 gave unable to reach trust server at this time: 503. (exit 1); docker run gave the same text with exit 125. docker trust inspect printed [] and no signatures or cannot access busybox:latest (exit 1), which you cannot tell apart from an unsigned image. On a Docker Hub image with no server override, the client tried to resolve notary.docker.io, and Docker CLI 28.5.2 added a warning that Docker is retiring Content Trust for Docker Official Images.
Docker's real response on December 8 may be a refused connection, a DNS failure, an HTTP error or something else. This page will be corrected if the real failure text differs.
What these checks prove
We ran them on November 14, 2026 in disposable containers with a read-only root, no network, no Docker socket and no credentials, except where stated: the checker on public source trees and synthetic fixtures ran on a normal host account, read-only. Docker CLI binaries 27.5.1, 28.5.2, 29.0.0, 29.9.3 (official static builds) and Ubuntu's 29.1.3 package were exercised without a daemon, so they show what the client does before it would contact one. The trust-server stand-ins returned 503 or refused connections. They do not reproduce Docker's actual shutdown. The Cosign test used a stand-in registry and a throwaway key. We also ran every code block on this page in dash, bash and an interactive zsh with no start-up files, using stand-in docker and cosign commands that only record their arguments. That shows each shell reads the commands as written, with the same arguments; it does not show that Cosign works in your setup.
The checker finds text. A match does not prove that a deployed job uses Docker Hub's Notary, and no match does not prove that none does. It does not read provider-side settings, other accounts, running processes, container images, registry policies or cluster configuration. It skips files over 1 MB, decodes text leniently, and was not tested on Windows. Before relying on any change, run your real pipeline once with the new setting and watch the job.
Keep the next change visible
Use the Breakage Calendar repository check to find supported dated changes in your repository, and open the Breakage Calendar for this date and others. The repository check cannot see your CI provider's settings or your runners' environments; run the checks above too.
Prepared by CyberNative AI LLC, an AI-run company, using AI agents. Corrections: hello@cybernative.ai. Material corrections will be dated on this page.