How to Fix "x509: certificate signed by unknown authority" in Docker
VigilDog Team · October 15, 2026 · 6 min read
You build a Docker image, run it, and a request to an internal API or registry dies with "x509: certificate signed by unknown authority." The certificate is valid, the server is fine, and yet your container refuses it. The error isn't lying: from inside that minimal image, the authority really is unknown. Here's what's actually happening and how to fix it without disabling verification.
What the error actually means
TLS clients trust a server by walking its certificate chain up to a root certificate they already hold in a local trust store. The x509 certificate signed by unknown authority error means that walk failed: somewhere in the chain is a CA your container has never heard of. The certificate is genuine; your container just wasn't given the root, or the intermediate, needed to verify it.
This is a container problem far more often than a certificate problem. Slim base images like alpine or distroless ship with a tiny or empty trust store, and internal CAs are never in the public bundle to begin with. So the same request that works on your laptop fails in the container, because your laptop's trust store is fuller than the image's.
The four usual causes in Docker
Before editing anything, figure out which of these you're hitting. The fix differs slightly for each, but they all come down to a missing certificate in the trust store.
- A corporate proxy that intercepts TLS (MITM). Your traffic is re-signed by an internal CA the container doesn't trust.
- A private registry or internal service using a self-signed or internal-CA certificate.
- A minimal base image (alpine, scratch, distroless) with no ca-certificates package installed at all.
- An incomplete chain: the server sends its leaf certificate but omits the intermediate, so no client can build a full path to a root.
The fix: add the CA to the image's trust store
For the first three causes, the fix is to bake the missing CA into the image. Get the root (and any intermediate) as a PEM file, copy it into the system trust directory, and rebuild the trust bundle. On Debian or Ubuntu bases, the file must end in .crt and be PEM-encoded:
COPY corp-root-ca.crt /usr/local/share/ca-certificates/corp-root-ca.crt then RUN update-ca-certificates. That command requires the ca-certificates package, so add RUN apt-get update && apt-get install -y ca-certificates first if it isn't already present. On Alpine it's RUN apk add --no-cache ca-certificates, then the same COPY into /usr/local/share/ca-certificates/ followed by update-ca-certificates.
One caveat: some runtimes ignore the OS store. Go binaries read it, but you can also point them at a file with SSL_CERT_FILE. Node needs NODE_EXTRA_CA_CERTS set to your PEM path. Whatever you do, don't reach for the shortcut of disabling verification (insecure-skip-verify, NODE_TLS_REJECT_UNAUTHORIZED=0), which trades a clear error for a silent hole.
When the chain itself is incomplete
If adding the root doesn't help, the server may be shipping an incomplete chain, the fourth cause, and no client-side trust store will fix that. Verify what the server actually sends with openssl s_client -showcerts -connect host:443. If you see the leaf certificate but no intermediate, the server is misconfigured, not your image.
The fix belongs on the server: install the full chain (leaf plus every intermediate, in order) so any client can build a path to a public root. Browsers often paper over this using cached intermediates, which is why it works in Chrome but fails in your container. Our free SSL checker reports a broken chain in plain language, and the guide on expired SSL certificates covers the neighboring failure mode where the cert is trusted but out of date.
Verify the fix inside the container
After rebuilding, prove it from inside the image rather than trusting that the Dockerfile ran. Exec into the container and run openssl s_client -connect host:443; a clean handshake ends with "Verify return code: 0 (ok)." If your app is Go or Node, retry the actual request, since those runtimes may need the env var pointed at your PEM even after the OS store is updated.
The deeper lesson is that trust stores and certificates both expire and drift. An internal CA gets rotated, an intermediate changes, a cert lapses, and a build that passed last quarter starts failing today. VigilDog's monitoring watches certificate chains and expiry across your domains and internal endpoints, so a chain that quietly breaks becomes an alert instead of a 3am container crash.
