docker/cli · error
error pinging v2 registry
Error message
error pinging v2 registry: %w
What it means
Root transport error from getHTTPTransport: registry.PingV2Registry GETs the /v2/ endpoint to learn the auth challenges, and any failure (connection refused, TLS error, non-2xx, non-v2 registry) is wrapped here. It is the concrete cause that 601 wraps, so most "failed to configure transport" reports trace back to this.
Solutions
- Run curl -v https://<host>/v2/ to see the exact HTTP/TLS failure.
- If the cert is private/self-signed, enable insecure mode (insecure=true / --insecure-registry).
- Confirm the registry software implements the OCI/Docker distribution v2 API.
- Verify DNS resolution and reachability (port open, no firewall).
- Re-authenticate with `docker login <host>` if the ping is failing on auth.
Example fix
$ curl -v https://registry.example.com/v2/ # if self-signed cert error, enable insecure mode: cli := registryclient.NewRegistryClient(resolver, ua, true)
Defensive patterns
Strategy: validation
Validate before calling
// Manual v2 ping mirroring what the client does internally.
func canPingV2(ctx context.Context, regURL string, insecure bool) error {
tr := &http.Transport{}
if insecure {
tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
c := &http.Client{Transport: tr, Timeout: 10 * time.Second}
req, _ := http.NewRequestWithContext(ctx, "GET", regURL+"/v2/", nil)
resp, err := c.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusUnauthorized {
return fmt.Errorf("unexpected v2 ping status %s", resp.Status)
}
return nil
} Try / catch
// Differentiate retryable network errors from fatal TLS/config errors.
if errors.Is(err, context.DeadlineExceeded) || isConnRefused(err) {
// retry with backoff
} else if isTLSerr(err) {
// prompt user about insecure mode / CA bundle
} Prevention
- Run a /v2/ preflight before operating on a registry.
- Install the registry's CA into the trust store, or enable insecure mode deliberately.
- Confirm the registry speaks distribution v2 (not a v1-only or plain HTTP server).
- Keep credentials valid via docker login.
When it happens
Trigger: The registry host is unreachable, the TLS certificate is invalid or self-signed (and insecure mode is off), the registry does not implement the distribution v2 API, or the /v2/ ping returns an error status before auth can proceed.
Common situations: Wrong registry URL; self-signed cert without insecure flag; registry behind a proxy that mangles /v2/; legacy v1-only registry; DNS failure; corporate firewall blocking the host.
Related errors
- failed to configure transport
- error establishing connection to trust repository
- no signatures or cannot access
- no signers for
- unable to get system cert pool
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/cdc31fd858a6538d.
Report an issue: GitHub.
Appendix: source
Thrown at internal/registryclient/endpoint.go:85
// getHTTPTransport builds a transport for use in communicating with a registry
func getHTTPTransport(authConfig registrytypes.AuthConfig, endpoint registry.APIEndpoint, repoName, userAgent string, actions []string) (http.RoundTripper, error) {
// get the http transport, this will be used in a client to upload manifest
base := &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 10 * time.Second,
TLSClientConfig: endpoint.TLSConfig,
DisableKeepAlives: true,
}
modifiers := registry.Headers(userAgent, http.Header{})
authTransport := transport.NewTransport(base, modifiers...)
challengeManager, err := registry.PingV2Registry(endpoint.URL, authTransport)
if err != nil {
return nil, fmt.Errorf("error pinging v2 registry: %w", err)
}
if authConfig.RegistryToken != "" {
passThruTokenHandler := &existingTokenHandler{token: authConfig.RegistryToken}
modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler))
} else {
if len(actions) == 0 {
actions = []string{"pull"}
}
creds := &staticCredentialStore{authConfig: &authConfig}
tokenHandler := auth.NewTokenHandler(authTransport, creds, repoName, actions...)
basicHandler := auth.NewBasicHandler(creds)
modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler))
}
return transport.NewTransport(base, modifiers...), nil
}
type existingTokenHandler struct {
token stringView on GitHub (pinned to 4f84911bfe)