<!-- https://pkizerotohero.com/errors/go-x509-unknown-authority/ | Markdown mirror of the HTML page | PKI Zero to Hero, by Duyutech BV -->

> Fix x509: certificate signed by unknown authority in Go, Docker and Kubernetes: trust the right root, serve the full chain, put CA bundles in images.

Source: https://pkizerotohero.com/errors/go-x509-unknown-authority/ · Last reviewed: 2026-10-20

- Go

# x509: certificate signed by unknown authority

Also seen in: Docker, Kubernetes (kubectl), Helm, Terraform, containerd, any Go program

**In one sentence:**

A Go program, such as Docker or kubectl, could not link the server's certificate to a trusted root: a private or proxy CA, a missing intermediate, or no CA bundle.

[Check yours: Chain checker](https://pkizerotohero.com/tools/chain/)

## Where you see it

In any program written in Go: Docker, `kubectl`, Helm, Terraform, containerd and many CLIs. The text is the `UnknownAuthorityError` of Go’s [`crypto/x509` package](https://pkg.go.dev/crypto/x509#UnknownAuthorityError). Programs built with Go 1.20 or later add the prefix `tls: failed to verify certificate:`; older builds print the message alone. Each tool adds its own context in front, for example Docker:

```text
Error response from daemon: Get "https://registry.example.com:5000/v2/": tls: failed to verify certificate: x509: certificate signed by unknown authority
```

and `kubectl`, in this example from the [Kubernetes troubleshooting guide](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/troubleshooting-kubeadm/):

```text
Unable to connect to the server: x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying to verify candidate authority certificate "kubernetes")
```

The part in brackets is an optional hint ([Go source](https://go.dev/src/crypto/x509/verify.go)): Go found a CA certificate with the right name, but its key does not match. That typically happens after a cluster or private CA was rebuilt; replace the old CA certificate in your store or kubeconfig with the new one.

## What it means

Go builds a [certificate chain](https://pkizerotohero.com/handbook/glossary/#certificate-chain) from the server’s certificate to a [root certificate](https://pkizerotohero.com/handbook/glossary/#root-certificate) in its [trust store](https://pkizerotohero.com/handbook/glossary/#trust-store). On Linux it reads the system CA bundle file; on Windows and macOS it asks the operating system. It found no trusted root for this chain. On Linux, Go does not download a missing intermediate, so a chain that works in a browser can still fail here.

## Likely causes

### A private CA or TLS-inspection proxy root is not trusted

Internal registries, Kubernetes API servers and Git servers often use a company CA, and a [TLS-inspection](https://pkizerotohero.com/handbook/glossary/#tls-inspection) proxy re-signs public sites. See who issued the certificate:

```bash
openssl s_client -connect registry.example.com:5000 -servername registry.example.com -showcerts </dev/null 2>/dev/null | grep -E '^ [0-9]+ s:|^   i:'
```

**Fix on a Linux host:** add the root to the system store, which Go reads:

```bash
# Debian, Ubuntu and Alpine (the file must end in .crt)
sudo cp corp-root.pem /usr/local/share/ca-certificates/corp-root.crt
sudo update-ca-certificates

# RHEL, Fedora and similar
sudo cp corp-root.pem /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust extract
```

On Linux, Go reads the first bundle it finds, such as `/etc/ssl/certs/ca-certificates.crt` or `/etc/pki/tls/certs/ca-bundle.crt`, and the `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables override where it looks. From Go 1.27 these variables also work on Windows and macOS, where they replace the operating system’s verifier.

**Docker daemon and a private registry:** put the registry’s CA in a folder named after the registry host and port, as described in Docker’s [registry certificate documentation](https://docs.docker.com/engine/security/certificates/). On Linux the folder is under `/etc/docker/certs.d/`; Docker Desktop uses `~/.docker/certs.d/` on the host:

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

The daemon treats every `.crt` file in that folder as a CA root. If you use the registry without a port number, leave the port out of the folder name too.

### Your container image has no CA certificates

Images built `FROM scratch`, and some minimal images, contain no CA bundle at all, so every HTTPS call fails. Copy one in:

```dockerfile
FROM alpine:3.20 AS certs
RUN apk add --no-cache ca-certificates

FROM scratch
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY app /app
ENTRYPOINT ["/app"]
```

To trust a company root inside Kubernetes pods, mount a bundle that contains the public roots **and** your root (for example from a ConfigMap) and point `SSL_CERT_FILE` at it:

```yaml
env:
  - name: SSL_CERT_FILE
    value: /etc/ssl/custom/ca-bundle.crt
volumeMounts:
  - name: ca-bundle
    mountPath: /etc/ssl/custom
    readOnly: true
# in the pod spec
volumes:
  - name: ca-bundle
    configMap:
      name: ca-bundle   # holds the key ca-bundle.crt
```

### kubectl has an outdated cluster CA

When your kubeconfig contains a cluster CA (`certificate-authority-data`), `kubectl` trusts only that CA, not the system store. After a cluster is rebuilt or its CA rotated, the old CA no longer matches. Compare the two:

```bash
kubectl config view --raw --minify -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' | base64 -d | openssl x509 -noout -subject -enddate
openssl s_client -connect api.cluster.example.com:6443 -showcerts </dev/null 2>/dev/null | grep -E '^ [0-9]+ s:|^   i:'
```

**Fix:** download a fresh kubeconfig from your provider or cluster administrator. If a TLS-inspection proxy sits in the way, ask for the API server to be excluded from inspection.

### The server leaves out its intermediate

Count the certificates the server sends (`grep -c 'BEGIN CERTIFICATE'` on the `-showcerts` output). If it is `1`, configure the server with its full chain: see [verify error:num=21](https://pkizerotohero.com/errors/openssl-verify-error-21/).

Warning

Why InsecureSkipVerify is a trap

`InsecureSkipVerify: true` in Go code, `kubectl --insecure-skip-tls-verify` and Docker’s `insecure-registries` setting all accept any certificate. For a registry that means you may pull and run an image from whoever intercepts the connection; for kubectl, you may hand cluster credentials to them. Trust the right CA instead.

## Check yours

*Interactive tool: Chain checker (runs in the browser at [/tools/chain/](https://pkizerotohero.com/tools/chain/))*

Paste the certificates from `openssl s_client -showcerts`. The checker shows the chain, its root, and any missing link.

## Learn why

- [Who do we trust? CAs, trust stores and self-signed certificates](https://pkizerotohero.com/handbook/trust-stores/)
- [Certificate chains and path validation](https://pkizerotohero.com/handbook/certificate-chains/)
- [A CA of your own: private two-tier PKI](https://pkizerotohero.com/handbook/private-ca/)
- See also: [x509: certificate has expired or is not yet valid](https://pkizerotohero.com/errors/go-x509-certificate-expired/).

**Early edition.** Checked against the standards; an independent expert review is in progress. Found a mistake? [Tell us](https://pkizerotohero.com/contact/?topic=content-error&page=/errors/go-x509-unknown-authority/).

Last reviewed 20 October 2026Edition 0.9

## Learn why it happens

- [Chapter 12: Who do we trust? CAs, trust stores and self-signed certificates](https://pkizerotohero.com/handbook/trust-stores/)
- [Chapter 13: Certificate chains and path validation](https://pkizerotohero.com/handbook/certificate-chains/)

## Fix it step by step

- [How to work behind a TLS-inspecting corporate proxy, safely](https://pkizerotohero.com/guides/corporate-proxy-certificates/)
- [How to trust a private root CA on Linux](https://pkizerotohero.com/guides/add-root-ca-linux/)
- [How to automate certificates in Kubernetes with cert-manager](https://pkizerotohero.com/guides/kubernetes-cert-manager/)
- [How to run a small private CA with step-ca (lab)](https://pkizerotohero.com/guides/private-ca-step-ca/)

## Everything on this topic

- [Chains and trust stores](https://pkizerotohero.com/topics/chains-and-trust/)
- [Private PKI](https://pkizerotohero.com/topics/private-pki/)
