The error "x509: certificate signed by unknown authority" means the client that made a connection does not trust the CA that signed the certificate the server presented. In Kubernetes, the quickest way to fix it is to work out which client is complaining (kubectl, an admission webhook, a node pulling an image, or a pod) and then repair the trust store that client uses. Switching verification off is not a fix.
This guide walks through that diagnosis step by step, gives the commands, and then covers the neighboring "TLS handshake timeout" error, which looks similar in a terminal but has a completely different cause.
TL;DR
- The message has one wording but at least three sources: a client talking to the API server, the API server talking to a webhook, and a node pulling from a registry. Identify which side is complaining first.
- For kubectl, the trust bundle is the
certificate-authority-datain your kubeconfig. A stale or wrong CA there is the most common cause. - Decode the CA, then compare its subject and dates with the issuer of the certificate the server actually presents.
- A "TLS handshake timeout" is a network-path problem, not a trust problem. Check port 443, security groups and routing before touching certificates.
- Repair trust distribution instead of disabling checks. Skipping verification hides the problem and leaves the connection open to interception.
Table of Contents
- What the error actually means
- Step one: find out who is complaining
- Fixing kubectl to API server errors
- Fixing webhook errors
- Fixing private registry and image pull errors
- Fixing errors inside pods
- Why not just skip verification?
- TLS handshake timeout: a different problem
- A quick decision path
- Perspective
- Work across clusters without losing track of trust
- Preventing the error from coming back
- FAQ
- Sources
What the error actually means
Every TLS client holds a set of certificate authorities it trusts. When a server presents a certificate, the client checks that it chains up to one of them. If it does not, the client stops and reports an unknown authority.
For kubectl, the set of trusted authorities is not your operating system's public roots. kubectl verifies the API server certificate against the cluster CA stored in your kubeconfig, in either certificate-authority or certificate-authority-data. The error appears when the kubeconfig has no CA, holds the CA of a different cluster, or when something between you and the API server presents its own certificate.
The same idea applies elsewhere. According to one error guide, for in-cluster clients the trust bundle is the service account ca.crt, and for a webhook it is the caBundle. Those are all just different places a CA list can live.
Step one: find out who is complaining
The error text is ambiguous, but the context around it is not. The same message shows up in three distinct places: a client connecting to the API server, the API server calling an admission webhook, and the kubelet pulling from a registry. Each has a different trust store and a different fix.
| Who is failing | Where the message appears | Trust store to check |
|---|---|---|
| kubectl or another API client | Your terminal, after kubectl get nodes | certificate-authority-data in kubeconfig |
| Admission webhook call | Events or API server errors when creating or updating objects | caBundle on the webhook configuration |
| Image pull | Pod events showing ErrImagePull or ImagePullBackOff | Container runtime or node OS certificates |
| App inside a pod | Application logs | The image's CA bundle or the file named by SSL_CERT_FILE |
| Kubelet or kubeadm | Node logs | kubelet.conf or files under /etc/kubernetes/ |
This mapping follows a table of callers and their trust stores. Use it as a checklist: where did you first see the message? A terminal means kubectl. A pod event means the kubelet and runtime. A failed kubectl apply that mentions a webhook means the API server calling out.
Fixing kubectl to API server errors
This is the case most people mean when they search for the error. You run kubectl get nodes and get Unable to connect to the server: x509: certificate signed by unknown authority.
Check which kubeconfig you are really using
Before anything else, confirm kubectl is reading the file you think it is. The KUBECONFIG environment variable can point to several files merged together, and an old context can shadow a new one. If you are unsure where the file lives on your system, our guide to the kubeconfig location on Windows, macOS and Linux lists the defaults.
kubectl config current-context
kubectl config view --minify
The --minify flag shows only the active context, including its server URL. Check that the URL is the endpoint you intended. A kubeconfig pointing at a node IP instead of the load balancer hostname is a frequent source of trouble.
Decode the CA and compare it to the server
Extract the embedded CA and read its details:
kubectl config view --raw --minify \
-o jsonpath='{.clusters[0].cluster.certificate-authority-data}' \
| base64 -d | openssl x509 -noout -subject -issuer -dates
Then look at what the server presents:
openssl s_client -connect API_HOST:6443 -showcerts </dev/null \
| openssl x509 -noout -subject -issuer -dates
Replace API_HOST and the port with your own endpoint (managed clusters often use 443). The approach matches the advice to decode certificate-authority-data and compare its subject and dates with the API server certificate's issuer. If the issuer of the served certificate does not match the subject of your CA, you have found the mismatch.
Common reasons the two do not match
- The cluster was rebuilt or its CA rotated. A stale kubeconfig after a cluster was recreated holds the old CA. Fetch a fresh kubeconfig from your provider or from the control plane.
- A self-signed API server with no CA distributed. The cluster works, but nobody gave your machine the CA file.
- Expired certificates. Per one guide, kubeadm control-plane certificates expire after 365 days. On a kubeadm cluster,
kubeadm certs check-expirationon the control plane shows the dates. - A SAN mismatch. The same guide notes that reaching the API server through a load balancer hostname or node IP that is not listed in
--apiserver-cert-extra-sanscan trigger certificate errors. - A TLS-inspecting proxy. Corporate proxies that re-sign traffic present their own certificate. Your client needs that proxy's CA, or the traffic needs to bypass inspection.
- A TLS terminator in front of the API. When an external TLS proxy or load balancer terminates TLS with a CA chain different from the cluster's internal root, clients must be configured to trust that external CA.
Apply the fix
Once you know which CA is missing or wrong, put the right one in the kubeconfig:
kubectl config set-cluster my-cluster \
--certificate-authority=/path/to/ca.crt \
--embed-certs=true
The principle is simple: make your kubeconfig match reality rather than turning off the check. Test with kubectl get nodes afterward.
Fixing webhook errors
When the API server calls an admission webhook and does not trust the webhook's certificate, object creation fails with the same x509 message, often quoting the webhook's service name. The trust store here is the caBundle field on the webhook configuration.
The usual causes are an empty or stale caBundle. Istio's documentation, for example, says these errors in webhook configurations are typically caused by an empty caBundle and recommends verifying that the caBundle in the mutating webhook configuration matches the root certificate mounted in the istiod pod.
A practical routine:
- Name the failing webhook from the error message.
- Run
kubectl get mutatingwebhookconfigurationsorvalidatingwebhookconfigurationsand open the entry with-o yaml. - Decode its
caBundleand compare the subject to the CA that signed the webhook server's certificate. - If the webhook's certificate was rotated (by cert-manager, for example), make sure the
caBundleis refreshed too. Many controllers inject it automatically; if yours does not, update it yourself.
If the webhook is blocking all deployments in the meantime, treat that as an incident and involve whoever owns the webhook before you change its failure policy.
Fixing private registry and image pull errors
The kubelet pulls images through the container runtime, so the registry's CA must be trusted by the node, not by your laptop. Symptoms are pods stuck in ErrImagePull with an x509 message in the events.
The repair is to install the registry's CA in the node trust store or the container runtime configuration. Depending on the runtime, that means a certs.d directory with a hosts.toml for containerd or CRI-O, or adding the CA to the node operating system's certificates and refreshing them. On managed node pools, bake the CA into the node image or startup script so new nodes do not regress.
Two details trip people up:
- Missing intermediates. If the registry's server leaves out the intermediate certificate, clients cannot build the chain. The fix is to concatenate the leaf and intermediate certificates into
tls.crtand update the secret, so the full chain is served. - Only one node fails. If one node pulls fine and another does not, the CA was probably installed by hand on one machine.
Fixing errors inside pods
An application in a pod calling an internal HTTPS service uses the CA bundle in its own image, not the cluster's. Add your internal CA to the image, mount it from a ConfigMap, and point the runtime at it with SSL_CERT_FILE or the language's equivalent. For calls to the Kubernetes API from inside a pod, use the service account's ca.crt mounted at /var/run/secrets/kubernetes.io/serviceaccount/.

Why not just skip verification?
Searching for this error turns up advice to set insecure-skip-tls-verify or --insecure-skip-tls-verify. It makes the message go away because the client stops checking who it is talking to. Anyone able to intercept the connection could then present their own certificate and capture your credentials and API traffic. The durable fix is always to repair trust distribution rather than disable checks.
If your API server is reachable only through a bastion host or tunnel, the certificate name often does not match the address you connect to locally. That is a certificate-name problem with a proper solution, such as adding the name to the server certificate's SANs or using a TLS server name override, not a reason to disable verification. Our article on Kubernetes SSH tunnels and certificate errors covers that scenario, and the broader API access hardening guide explains why exposure choices matter.

TLS handshake timeout: a different problem
Readers who hit the x509 error often also see net/http: TLS handshake timeout. The two are easy to mix up, but they happen at different stages.
An x509 unknown authority error means the handshake progressed far enough for the server to send a certificate, and the client rejected it. A handshake timeout means the connection never got that far. One analysis describes it as the TCP connection to the endpoint never completing, for instance because a security group, network ACL or missing route dropped the packets. That is different from a 401 or 403 response, which proves the request reached its destination. Another thread notes that a TLS handshake timeout occurs before any HTTP payload is available to examine.
A third error, tls: handshake failure, is also separate. It means negotiation failed before any certificate was exchanged: no shared protocol version or cipher suite, a missing SNI value, or a required client certificate that was not offered.
| What you see | Where it failed | First thing to check |
|---|---|---|
x509: certificate signed by unknown authority | Certificate was received but not trusted | The CA in the client's trust store |
TLS handshake timeout | Network path, before the handshake completes | Reachability of the endpoint, firewalls, routing |
tls: handshake failure | Negotiation, before certificates | TLS versions, ciphers, SNI, client certificate requirements |
Troubleshooting a handshake timeout
For an Amazon EKS cluster, AWS's guidance is to test connectivity to the API endpoint on port 443 and to check the source CIDR, security groups and network ACLs, as well as whether private endpoint access is coming from inside the VPC or a connected network. The same page lists blocked port 443 from security groups or network ACLs among the common causes of API connection timeouts.
On any provider, the sequence is the same:
- Resolve the API hostname and confirm it returns the address you expect.
- Test a raw TCP connection to the port, for example with
nc -vz API_HOST 443. - If TCP fails, look at firewalls, security groups, VPN state and whether you are on the network allowed to reach a private endpoint.
- If TCP succeeds but the handshake stalls, look at proxies, MTU problems and overloaded or unhealthy control plane endpoints.
The key skill is identifying the specific hop where a failure occurs by mapping error strings to causes instead of guessing. For a fuller layered approach, see our Kubernetes connectivity troubleshooting guide.
A quick decision path
When you are in the middle of an incident, work through this in order:
- Read the full error. Note the client, the target host and any webhook or registry name.
- Classify it. Terminal kubectl error, webhook, image pull, pod app, or node component.
- Compare the CA. Decode what the client trusts and compare it with what the server presents.
- Check dates and names. Expired certificates and SAN mismatches look like trust problems but need renewal or reissue.
- Fix the trust store, not the check. Update the kubeconfig,
caBundle, runtime config or image bundle. - Make it stick. Automate CA distribution so a rebuild or rotation does not bring the error back.
Perspective
Most of the x509 tickets we see come down to one thing: the engineer has several clusters, several kubeconfig files and a few tunnels, and the one in use quietly no longer matches the cluster on the other end. The certificate is rarely "broken." The client is just looking at the wrong trust data.
That is why we built Kubezilla Desktop around local-first handling of kubeconfigs. They stay stored securely on your machine, there is no cloud sync or telemetry, and every cluster you connect to sits in one native interface, so you can see which cluster and which connection you are working with instead of guessing from a shell prompt. Built in Rust for low memory use and fast startup, it keeps that visibility cheap.
For clusters behind a bastion, built-in SSH tunneling means you connect to the remote cluster directly rather than juggling separate tunnel commands and kubeconfig edits. Fewer moving parts around the connection means fewer places for trust to drift. We think that is a better answer to certificate errors than any flag that turns verification off.
Margus
Work across clusters without losing track of trust
If you manage several clusters, the hardest part of x509 errors is knowing which kubeconfig, context and tunnel you are using at the moment of failure. Kubezilla Desktop is a native app for Linux, macOS and Windows that keeps your kubeconfigs in secure local storage, connects to multiple clusters, supports SSH tunnels, and gives you logs, metrics, pod terminals, YAML editing, and Helm and ArgoCD operations in one place. It costs €5 per month.

Read more about the Kubernetes client to see how it fits into your daily workflow.
Preventing the error from coming back
A few habits remove most repeat incidents:
- Track certificate expiry. Calendar the renewal for kubeadm clusters, since control-plane certificates last a year by default.
- Refresh kubeconfigs after any rebuild or CA rotation. Do not copy an old file across to a new cluster.
- Serve the full chain. Include intermediates in the server's certificate file.
- Distribute CAs centrally. Use node images, startup scripts or configuration management for registry and proxy CAs.
- Automate
caBundleupdates. Let your certificate controller inject them for webhooks. - Use stable API names. Connect through a hostname listed in the server certificate's SANs rather than a raw IP that might change.
FAQ
Is it safe to use --insecure-skip-tls-verify to get past the error?
It removes the symptom but also removes the protection, because the client no longer verifies who it is talking to. Anyone who can intercept the connection could present their own certificate. Fix the CA in your kubeconfig, webhook or node instead.
Why does kubectl say unknown authority when my browser trusts the same site?
Because kubectl does not use your system's public roots to verify the API server. It checks against the cluster CA in your kubeconfig. If that CA is missing, outdated or from another cluster, verification fails even though a browser would be fine.
How do I tell a TLS handshake timeout from a certificate error?
A certificate error means the server answered with a certificate that the client rejected. A timeout means the connection never completed, so the cause is usually a blocked port, firewall or routing issue. Test TCP reachability to port 443 or 6443 first, then look at certificates only if the connection succeeds.
The error appeared right after we rebuilt the cluster. What changed?
The rebuild probably generated a new CA, and your kubeconfig still holds the old one. Download or generate a fresh kubeconfig and replace the old entry, or update the cluster's certificate-authority-data. Then confirm with kubectl config view --minify that the server URL is also current.
The error only happens when I create certain resources. Is that a kubeconfig problem?
Likely not. If the message names a webhook service, the API server is failing to trust the webhook's certificate. Check the webhook configuration's caBundle against the CA that signed the webhook server's certificate, and refresh it if the certificate was rotated.
Sources
- Kubernetes Error Guide: x509 certificate signed by unknown authority (DevOps AI Toolkit)
- Kubernetes Error Guide: remote error: tls: handshake failure (DevOps AI Toolkit)
- x509: certificate signed by unknown authority (kubectl) (ECDSA.com)
- x509: certificate signed by unknown authority (kubeerrors.com)
- x509: certificate signed by unknown authority (Kubernetes Interview Questions)
- Fix kubectl x509: certificate signed by unknown authority (Coding Protocols)
- Troubleshooting tls: failed to verify certificate x509 (IBM Support)
- Sidecar Injection Problems (Istio 1.26 documentation)
- Diagnosing Certificate and TLS Handshake Failures (Cursa)
- ECR Cross-Account TLS Handshake Debugger (StackEngine)
- kubectl version times out with TLS handshake timeout (Kubernetes Discussion)
- Troubleshoot issues with kubectl cluster connection (AWS re:Post)
