Change:
} bootstrapClientConfig, err := loadRESTClientConfig(kubeconfigPath) if err != nil { <del> utilruntime.HandleError(fmt.Errorf("unable to read existing bootstrap client config: %v", err)) </del> <ins> utilruntime.HandleError(fmt.Errorf("unable to read existing bootstrap client config from %s: %v", kubeconfigPath, err)) </ins> return false, nil } transportConfig, err := bootstrapClientConfig.TransportConfig() if err != nil { <del> utilruntime.HandleError(fmt.Errorf("unable to load transport configuration from existing bootstrap client config: %v", err)) </del> <ins> utilruntime.HandleError(fmt.Errorf("unable to load transport configuration from existing bootstrap client config read from %s: %v", kubeconfigPath, err)) </ins> return false, nil } // has side effect of populating transport config data fields if _, err := transport.TLSConfigFor(transportConfig); err != nil { <del> utilruntime.HandleError(fmt.Errorf("unable to load TLS configuration from existing bootstrap client config: %v", err)) </del> <ins> utilruntime.HandleError(fmt.Errorf("unable to load TLS configuration from existing bootstrap client config read from %s: %v", kubeconfigPath, err)) </ins> return false, nil } certs, err := certutil.ParseCertsPEM(transportConfig.TLS.CertData) if err != nil { <del> utilruntime.HandleError(fmt.Errorf("unable to load TLS certificates from existing bootstrap client config: %v", err)) </del> <ins> utilruntime.HandleError(fmt.Errorf("unable to load TLS certificates from existing bootstrap client config read from %s: %v", kubeconfigPath, err)) </ins> return false, nil } if len(certs) == 0 { <del> utilruntime.HandleError(fmt.Errorf("unable to read TLS certificates from existing bootstrap client config: %v", err)) </del> <ins> utilruntime.HandleError(fmt.Errorf("unable to read TLS certificates from existing bootstrap client config read from %s: %v", kubeconfigPath, err)) </ins> return false, nil } now := time.Now() for _, cert := range certs { if now.After(cert.NotAfter) { <del> utilruntime.HandleError(fmt.Errorf("part of the existing bootstrap client certificate is expired: %s", cert.NotAfter)) </del> <ins> utilruntime.HandleError(fmt.Errorf("part of the existing bootstrap client certificate in %s is expired: %v", kubeconfigPath, cert.NotAfter)) </ins> return false, nil } }

Discussion:
What happened: the fails to start; it logs, What you expected to happen: either the starts, or it produces a useable diagnostic It tells me that a certificate is expired. If you Google the error, you'll hit SO questions like , which helpfully suggest you run ¹. That runs successfully, but afterwards, the continues to fail to start. A bit more digging, and one might learn about , and think, ah-ha! That'll tell me which cert has expired, and thus, where to look for guidance on renewing it. As we've already run , it will tell us that … …nothing is expired. (But still the refuses to start.) So we just run every in through manually with : …and they're all fine. After some further digging, I did find it. It's in , but it's wrapped in YAML config so the can't see it. It isn't clear to me why should miss it. But at the very least, needs to emit a usable diagnostic, minimally including which* certificate is expired, and ideally with enough information for me to determine the correct way to fix it. (Eventually, I hit , which contains the additional command to regenerate .) How to reproduce it (as minimally and precisely as possible): run a cluster for ~1 yr, let the cert expire… Anything else we need to know?: ¹it instills much confidence that renewing the certs is "alpha". Environment: Kubernetes version (use ): 1.16.8 Cloud provider or hardware configuration: N/A, single -made node OS (e.g: ): Gentoo Kernel (e.g. ): Install tools: ? Network plugin and version (if this is a network-related bug): N/A, but Calico Others:
This issue is currently awaiting triage. If a SIG or subproject determines this is a relevant issue, they will accept it by applying the label and provide further guidance. The label can be by org members by writing in a comment. <detailsInstructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
It seems that the certs are renewed today. You may need to restart kube-apiserverkube-controllerkube-scheduleretcd & kubelet on each node.

It's suggested to run before it expired.
this version is no longer in support. you seem to have sufficient information on how to recover. arguably it does need better messaging. i will keep this open with a "feature" label, but if you want this changed you might have to send the PR yourself. the guide to create certs is here: kubeadm makes it easier for users, but the kubelet error messages should not mention kubeadm. /remove-kind bug /kind feature /area kubelet
/sig node
/retitle kubelet: improve error messages about expired certificates
also leaving this to SIG Node to decide if the want the feature or not. (via "triage accepted" vs "close")
If you don't mind, I sent a PR to improve the error message.