Skip to content
RU

x509: Certificate Signed by Unknown Authority (Go, Docker, kubectl): How to Fix

What it means: Go could not chain the server's certificate to any root in its trust pool.

  1. Check the image has CA certificates
  2. Read who issued the server certificate
  3. Private CA: add its root to the pool
  4. Docker registry: use certs.d
  5. Never set InsecureSkipVerify

Test your URL now

Runs a full TLS check: handshake, protocol versions, certificate chain, trust and expiry.

Check your site's SSL →

What x509: certificate signed by unknown authority means

The text is the Error() string of Go's x509.UnknownAuthorityError: «x509: certificate signed by unknown authority» (crypto/x509/verify.go). Go built the chain from what the server sent and could not reach any root in the certificate pool it was given. Since Go 1.20, crypto/tls wraps verification failures in CertificateVerificationError, so the full line usually reads tls: failed to verify certificate: x509: certificate signed by unknown authority (Go 1.20 release notes).

Sometimes a hint follows: (possibly because of "..." while trying to verify candidate authority certificate "..."). Go found a certificate that looked like the right issuer, but checking its signature failed, for example because of an unsupported algorithm or a mismatched key. That hint points at the chain contents rather than at a missing root.

Because Docker, Kubernetes tooling, Terraform, Helm and many CLIs are written in Go, the same text appears in docker pull, kubectl and others. Other languages report the same situation as unable to get local issuer certificate (OpenSSL) or PKIX path building failed (Java).

Causes

CauseWhere you see itFix
No CA certificates in the containerBinary in FROM scratch or a minimal image; every HTTPS call failsCopy or install a CA bundle in the image
Private or self-signed CA on the serverInternal APIs, registries, Vault, test clustersAdd that root to the system store or the client's pool
TLS-inspecting proxyWorks at home, fails on the office network or CITrust the proxy's root explicitly
Server omits the intermediateBrowsers work, Go clients failServe the full chain on the server
kubeconfig CA does not match the API serverkubectl after a cluster rebuild or certificate rotationRefresh certificate-authority-data

On Linux Go reads the first CA file it finds from a fixed list, including /etc/ssl/certs/ca-certificates.crt (Debian, Ubuntu), /etc/pki/tls/certs/ca-bundle.crt (Fedora, RHEL) and /etc/ssl/cert.pem (Alpine), plus certificate directories (crypto/x509/root_linux.go). A scratch image has none of them, so a statically linked Go binary that works on your laptop fails with this error in the container. The SSL_CERT_FILE and SSL_CERT_DIR environment variables override those locations (crypto/x509 SystemCertPool).

Docker, registries and kubectl

docker pull registry.example.com:5000/app fails with this error when the registry uses a private CA. The Docker daemon reads extra roots from a directory named after the registry: on a native Linux engine that is /etc/docker/certs.d/registry.example.com:5000/ca.crt, and every *.crt file there is added as a CA root (Docker documentation). Rootless Docker uses ~/.config/docker/certs.d instead, and Docker Desktop takes them from ~/.docker/certs.d on the host.

sudo mkdir -p /etc/docker/certs.d/registry.example.com:5000
sudo cp registry-root.pem /etc/docker/certs.d/registry.example.com:5000/ca.crt
docker pull registry.example.com:5000/app

For kubectl, the trusted CA is the certificate-authority-data field of the cluster entry in your kubeconfig. If the cluster was rebuilt or its CA rotated, fetch a fresh kubeconfig from the cluster's provisioning tool instead of adding insecure-skip-tls-verify.

How to fix it

  1. See who signed the server certificate:
    openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null | grep -E '^ *[0-9] s:|^ *i:'
    A public CA means the client lacks roots or the server lacks an intermediate; a company or product name means a private CA or a proxy.
  2. Containers: add the bundle to the image. Multi-stage build into scratch:
    FROM golang:1.23 AS build
    # ... go build ...
    FROM scratch
    COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
    COPY --from=build /app /app
    On Alpine or Debian bases, install the ca-certificates package.
  3. Private CA, system-wide: add the root to the OS store (update-ca-certificates or update-ca-trust), or point the process at a bundle with SSL_CERT_FILE=/etc/ssl/private/bundle.pem.
  4. Private CA, in code: start from the system pool and append, so public sites keep working:
    pool, _ := x509.SystemCertPool()
    pool.AppendCertsFromPEM(rootPEM)
    client := &http.Client{Transport: &http.Transport{
        TLSClientConfig: &tls.Config{RootCAs: pool},
    }}
  5. Do not set InsecureSkipVerify: true. It disables chain and host name checks for every connection that config makes.

How to confirm the fix

docker run --rm your-image   # the request that failed should now succeed
openssl s_client -connect api.example.com:443 -servername api.example.com -CAfile /etc/ssl/certs/ca-certificates.crt </dev/null 2>/dev/null | grep 'Verify return code'
# Verify return code: 0 (ok)

For a public endpoint, the SSL/TLS checker shows whether the chain your server sends reaches a publicly trusted root, which tells you whether to fix the server or the client.

Related errors

CertificateExpiry, issuer, domains (SAN)
ChainIntermediate and root CA validation
TLS ProtocolTLS version and cipher suite
VulnerabilitiesHeartbleed, POODLE, weak ciphers

Why teams trust us

TLS 1.3
supported
Full
CA chain check
3,158
checks in 30 days
30/14/7
days-to-expiry alerts

How it works

1

Enter domain

2

TLS chain verified

3

Expiry date & vulnerabilities

What Does the SSL Check Cover?

SSL/TLS is the encryption protocol that protects data between the browser and server. Our tool analyzes the certificate, chain of trust, TLS version, and knownvulnerabilities.

Certificate Details

Issuer, validity period, signature algorithm, covered domains (SAN), and validation type (DV/OV/EV).

Chain of Trust

Full chain verification: from leaf certificate through intermediates to root CA.

TLS Analysis

Protocol version (TLS 1.2/1.3), cipher suites, Perfect Forward Secrecy (PFS) support.

Expiry Alerts

Set up a monitor — get Telegram and email alerts 30/14/7 days before expiration.

DV vs OV vs EV Certificates

DV (Domain Validation)
  • Confirms domain ownership only
  • Issued in minutes automatically
  • Free via Let's Encrypt
  • Suitable for most websites
  • Most common certificate type
OV / EV
  • Organization (OV) or Extended Validation (EV)
  • Issued in 1-5 business days
  • Costs $50 to $500/year
  • For finance, e-commerce, government sites
  • Increases user trust

Who uses this

DevOps

SSL certificate monitoring

Security

TLS config audit

SEO

HTTPS as ranking factor

E-commerce

customer trust

Common Mistakes

Expired certificateBrowsers block sites with expired SSL. Set up auto-renewal or monitoring.
Incomplete certificate chainWithout intermediate CA, some browsers and bots cannot verify the certificate.
Mixed content on HTTPS siteHTTP resources on an HTTPS page — the browser lock icon disappears, reducing trust.
Using TLS 1.0/1.1Legacy TLS versions have known vulnerabilities. Use TLS 1.2+ or 1.3.
Domain mismatch in certificateThe certificate must cover all site domains, including www and subdomains.

Best Practices

Set up auto-renewalLet's Encrypt + certbot with cron — certificate renews automatically every 60-90 days.
Enable HSTSStrict-Transport-Security header forces browsers to always use HTTPS.
Use TLS 1.3TLS 1.3 is faster (1-RTT handshake) and safer — legacy ciphers removed.
Monitor expiration datesCreate a monitor on Enterno.io — get notified well before expiration.
Verify chain after renewalAfter certificate renewal, confirm that intermediate certificates are installed.

Get more with a free account

SSL certificate monitoring, check history and alerts 30 days before expiry.

Sign up free

Learn more

Sources

Frequently Asked Questions

Why does my Go binary work locally but fail in Docker with certificate signed by unknown authority?

The container has no CA bundle, which is typical for scratch and very small images. Copy /etc/ssl/certs/ca-certificates.crt into the image or install the ca-certificates package.

How do I fix x509 certificate signed by unknown authority in docker pull?

Put the registry's root certificate in /etc/docker/certs.d/registry-host:port/ca.crt on a native Linux engine. The daemon trusts every .crt file in that directory for that registry.

Is InsecureSkipVerify an acceptable fix?

No. It turns off certificate and host name verification, so any machine in the path can impersonate the server. Add the right root to RootCAs instead.

What does tls: failed to verify certificate mean in newer Go?

Since Go 1.20 crypto/tls wraps verification errors in CertificateVerificationError, which adds that prefix. The cause is the x509 error after it.

Can SSL_CERT_FILE fix it without rebuilding?

On Linux, yes: SSL_CERT_FILE and SSL_CERT_DIR override where Go looks for system roots. Point SSL_CERT_FILE at a bundle that contains the missing root.

Try the live tool that powered this guide

Free plan — 10 monitors, checks every 5 min, no card required. Upgrade for 1-minute interval and multi-region monitoring.