docker/cli · error
error establishing connection to trust repository
Error message
error establishing connection to trust repository: %w
What it means
In PushTrustedReference (trust_push.go:102-104), GetNotaryRepository failed to construct the authenticated notary client/transport, and the error is wrapped with 'error establishing connection to trust repository'. GetNotaryRepository (trust.go:119-201) builds the TLS config, reads the cert directory, pings the notary /v2/ endpoint, and sets up token/basic auth - any of those failing yields this wrap.
Solutions
- Verify DOCKER_CONTENT_TRUST_SERVER is a valid https URL and reachable: curl -vk <server>/v2/ should return 200 or 401.
- For a private notary with a self-signed cert, place the CA cert at ~/.docker/tls/<host>/ca.crt (and client.crt/client.key if mTLS) and retry.
- Re-authenticate to the registry: docker login <registry> so the token handler has valid credentials.
- Check network/proxy: ensure HTTPS_PROXY/HTTP_PROXY allow traffic to the notary server and DNS resolves the host.
- If repoInfo.Index.Secure is false, the client skips TLS verification (InsecureSkipVerify) - confirm that is intentional rather than masking a cert problem.
Example fix
# before: self-signed notary cert, connection fails export DOCKER_CONTENT_TRUST_SERVER=https://notary.internal:4443 DOCKER_CONTENT_TRUST=1 docker push registry.internal/img:tag # after: install the CA so TLS verifies mkdir -p ~/.docker/tls/notary.internal:4443 cp /etc/notary/root-ca.crt ~/.docker/tls/notary.internal:4443/ca.crt DOCKER_CONTENT_TRUST=1 docker push registry.internal/img:tag
Defensive patterns
Strategy: try-catch
Validate before calling
// Validate notary server reachability and TLS before the real push.
func preflightNotaryServer(server string, authCfg *registrytypes.AuthConfig) error {
if server == "" {
return errors.New("DOCKER_CONTENT_TRUST_SERVER is empty")
}
u, err := url.Parse(server)
if err != nil || u.Scheme != "https" {
return fmt.Errorf("trust server must be https, got %s", server)
}
client := &http.Client{Timeout: 5 * time.Second}
req, _ := http.NewRequest(http.MethodGet, server+"/v2/", nil)
if authCfg.Username != "" {
req.SetBasicAuth(authCfg.Username, authCfg.Password)
}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("cannot reach notary server %s: %w", server, err)
}
resp.Body.Close()
return nil
} Try / catch
repo, err := trust.GetNotaryRepository(in, out, ua, repoInfo, authCfg, "push", "pull")
if err != nil {
return fmt.Errorf("error establishing connection to trust repository: %w", err)
} Prevention
- Install the notary server CA under ~/.docker/tls/<host>:<port>/ca.crt for private registries.
- Always set DOCKER_CONTENT_TRUST_SERVER to an https URL.
- Run 'docker login' before trust operations so the token handler can authenticate.
- Add a connectivity preflight (curl /v2/) in CI before trusted pushes.
- Ensure corporate proxies allow traffic to the notary host.
When it happens
Trigger: The /v2/ ping to the notary server fails or the challenge manager cannot parse the response; TLS handshake fails because the server cert is untrusted or the local tls/<host> cert directory has bad certs; DOCKER_CONTENT_TRUST_SERVER is set to a non-https URL (rejected by Server()); registry auth credentials are missing so the token handler cannot authenticate; certificateDirectory url.Parse fails.
Common situations: Self-hosted notary server with a self-signed cert that is not installed under ~/.docker/tls/<host>; DOCKER_CONTENT_TRUST_SERVER typo or http:// (must be https); behind a corporate proxy that blocks the notary endpoint; 'docker login' credentials expired so token auth returns 401; clock skew invalidating TLS; DNS resolution failure for the notary host.
Related errors
- no signatures or cannot access
- no signers for
- error: remote trust data does not exist for
- failed to configure transport
- error pinging v2 registry
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/f544f19949da5b02.
Report an issue: GitHub.
Appendix: source
Thrown at cmd/docker-trust/internal/trust/trust_push.go:104
}
if err := jsonstream.Display(ctx, in, ioStreams.Out(), jsonstream.WithAuxCallback(handleTarget)); err != nil {
return err
}
if cnt > 1 {
return errors.New("internal error: only one call to handleTarget expected")
}
if notaryTarget == nil {
return errors.New("no targets found, provide a specific tag in order to sign it")
}
_, _ = fmt.Fprintln(ioStreams.Out(), "Signing and pushing trust metadata")
repo, err := GetNotaryRepository(ioStreams.In(), ioStreams.Out(), userAgent, repoInfo, &authConfig, "push", "pull")
if err != nil {
return fmt.Errorf("error establishing connection to trust repository: %w", err)
}
// get the latest repository metadata so we can figure out which roles to sign
_, err = repo.ListTargets()
switch err.(type) {
case client.ErrRepoNotInitialized, client.ErrRepositoryNotExist:
keys := repo.GetCryptoService().ListKeys(data.CanonicalRootRole)
var rootKeyID string
// always select the first root key
if len(keys) > 0 {
sort.Strings(keys)
rootKeyID = keys[0]
} else {
rootPublicKey, err := repo.GetCryptoService().Create(data.CanonicalRootRole, "", data.ECDSAKey)
if err != nil {
return err
}View on GitHub (pinned to 4f84911bfe)