Pulse 2025 Product Roundup: From Monitoring to AI-Native Control Plane

Read more

Elasticsearch TLS Certificate Expired or Untrusted

Elasticsearch 8.x enables TLS on both the transport and HTTP layers by default, using auto-generated certificates signed by a local CA created during node setup. Those certificates have a finite lifetime - typically 3 years for auto-generated ones. When they expire, inter-node communication breaks, clients get rejected, and Kibana loses its connection. The failure mode is abrupt: everything works until the expiry timestamp passes, then nothing does.

Detecting Expired Certificates

The fastest way to check certificate expiry from outside the cluster is openssl. For the HTTP layer:

openssl s_client -connect localhost:9200 </dev/null 2>/dev/null | openssl x509 -noout -dates

This prints notBefore and notAfter timestamps. For PKCS#12 keystores stored on disk (the default format for auto-generated certs), extract the expiry date directly:

openssl pkcs12 -in /etc/elasticsearch/certs/http.p12 -clcerts -nokeys -passin pass: \
  | openssl x509 -noout -enddate

Elasticsearch logs surface expired certificates with specific exceptions. Look for java.security.cert.CertificateExpiredException followed by a notAfter date, or SSLHandshakeException entries containing validity check failed. On the transport layer, expired certs produce io.netty.handler.codec.DecoderException wrapping an SSL handshake failure - nodes simply refuse to form a cluster. The _ssl/certificates API shows all loaded certificates with their expiry dates in one call:

GET /_ssl/certificates

Each entry includes path, format, alias, subject_dn, expiry, and has_private_key. Parse the expiry field programmatically and alert well before the date arrives.

Renewing Certificates with elasticsearch-certutil

The elasticsearch-certutil tool handles certificate generation for both transport and HTTP layers. If your existing CA is still valid, generate new node certificates signed by the same CA:

bin/elasticsearch-certutil cert \
  --ca elastic-stack-ca.p12 \
  --ca-pass "" \
  --days 1095 \
  --out /etc/elasticsearch/certs/new-http.p12 \
  --pass ""

For the HTTP layer specifically, the http subcommand walks through an interactive flow that produces a zip containing both the Elasticsearch keystore and a CA certificate for Kibana:

bin/elasticsearch-certutil http

This generates elasticsearch-ssl-http.zip with separate directories for Elasticsearch and Kibana configuration. If you need non-interactive generation for automation, use the cert subcommand with explicit flags and feed the output into your config management tooling.

When the CA itself is close to expiry, generate a new one first with elasticsearch-certutil ca, then issue new node certificates from it. Keep the old CA in the truststore alongside the new one during the transition window.

Rolling Certificate Update Without Downtime

Elasticsearch monitors TLS resource files every five seconds and reloads them automatically when changes are detected on disk. This makes zero-downtime certificate rotation possible if you follow the right sequence.

For certificates using the same filename, copy the new keystore over the old one on each node. Elasticsearch detects the file change and loads the new certificate. No restart required. The catch: if the keystore password changes, Elasticsearch cannot reload it in place because the password is stored in the secure settings keystore, which requires a restart to update. Keep the same password (or no password) on the new keystore to avoid this.

The recommended sequence for a multi-node cluster is straightforward. Replace the certificate file on one node at a time. Wait for the _ssl/certificates API to reflect the new expiry date on that node before proceeding to the next. If you are changing passwords or filenames, a rolling restart is required instead - stop the node, update elasticsearch.yml with the new path and update the secure settings via elasticsearch-keystore, then start it again. Do this one node at a time, waiting for the cluster to return to green status between each restart.

CA Rotation Procedure

Rotating the CA is more involved than swapping node certificates because every node and client must trust both the old and new CA during the transition.

Start by generating a new CA with elasticsearch-certutil ca. Then add the new CA certificate to the existing truststore on every node. In PEM mode, this means appending the new CA cert to your ca.pem bundle file. In PKCS#12 mode, import the new CA into the existing truststore using keytool:

keytool -importcert -trustcacerts -alias new-ca \
  -file new-ca.crt -keystore truststore.p12

After all nodes trust both CAs, generate new node certificates signed by the new CA and deploy them using the rolling update procedure described above. Only after every node is running certificates signed by the new CA should you remove the old CA from the truststores. Removing it prematurely causes nodes still presenting old-CA-signed certificates to be rejected by nodes that no longer trust the old CA - the cluster partitions instantly.

For clients (Kibana, Logstash, Beats, application code), update their CA certificate or truststore in parallel with the node rollout. Kibana in particular stores its elasticsearch.ssl.certificateAuthorities path in kibana.yml and needs a restart after the CA file changes.

Common Errors and What They Mean

PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException - The certificate presented by the remote side is not signed by any CA in the local truststore. Either the CA certificate is missing from the truststore, the CA has been rotated without updating the truststore, or a self-signed certificate is in use without being explicitly trusted.

SSLHandshakeException: General SSLEngine problem - A catch-all wrapper for TLS negotiation failures. Check the nested cause. Common nested exceptions include CertificateExpiredException (the certificate's notAfter date has passed), CertificateNotYetValidException (clock skew - the node's system clock is before the cert's notBefore date), and CertPathValidatorException (chain of trust is broken).

io.netty.handler.ssl.NotSslRecordException: not an SSL/TLS record - A plaintext HTTP request was sent to a TLS-enabled port, or vice versa. This is not a certificate problem - it is a protocol mismatch, typically from a client connecting via http:// to a node listening on HTTPS.

certificate_verify_failed in Python/Ruby/curl clients - The client cannot verify the server certificate. Either pass the CA certificate explicitly (--cacert in curl, ca_certs parameter in the Python client) or, for self-signed setups during development only, disable verification. Never disable verification in production.

Elasticsearch 8.x auto-generated certificates use a CA stored at elastic-stack-ca.p12 in the Elasticsearch config directory. This CA is created once during initial node setup and shared (manually) to other nodes joining the cluster. The auto-generated node certificates reference this CA. When these expire, the renewal process is identical to the manual process above - generate new certs from the same CA, or rotate the CA if it has also expired. The elasticsearch-reconfigure-node tool does not renew certificates; it only reconfigures security settings for a node rejoining a cluster.

Pulse - Elasticsearch Operations Done Right

Pulse can solve your Elasticsearch issues

Subscribe to the Pulse Newsletter

Get early access to new Pulse features, insightful blogs & exclusive events , webinars, and workshops.

We use cookies to provide an optimized user experience and understand our traffic. To learn more, read our use of cookies; otherwise, please choose 'Accept Cookies' to continue using our website.