rqlite/rqlite · critical

cannot create TLS config: %s

Error message

cannot create TLS config: %s

What it means

Returned by newTLSMux (via NewTLSMux / NewMutualTLSMux) when rtls.CreateServerConfigWithFunc fails to build the *tls.Config from the certificate reloader and CA settings. This happens after the cert reloader was created successfully, so the failure is in the CA certificate, mutual-TLS state, client-CN verification settings, or the generated config itself. Node-to-node TLS cannot be enabled without a valid config.

Source

Thrown at tcp/mux.go:154

	}

	mtlsState := rtls.MTLSStateDisabled
	if mutual {
		mtlsState = rtls.MTLSStateEnabled
	}
	mux.certReloader, err = rtls.NewCertReloader(cert, key)
	if err != nil {
		return nil, fmt.Errorf("cannot create cert monitor: %s", err)
	}

	// Wrap the GetCertificate function so we update the stats.
	getCertFunc := func() (*tls.Certificate, error) {
		stats.Add(numTLSCertFetched, 1)
		return mux.certReloader.GetCertificate()
	}
	mux.tlsConfig, err = rtls.CreateServerConfigWithFunc(getCertFunc, caCert, mtlsState, verifyCN)
	if err != nil {
		return nil, fmt.Errorf("cannot create TLS config: %s", err)
	}

	mux.ln = tls.NewListener(ln, mux.tlsConfig)
	return mux, nil
}

// Serve handles connections from ln and multiplexes then across registered listener.
func (mux *Mux) Serve() error {
	tlsStr := ""
	if mux.tlsConfig != nil {
		tlsStr = "TLS "
	}
	mux.Logger.Printf("%smux serving on %s, advertising %s", tlsStr, mux.ln.Addr().String(), mux.addr)

	for {
		// Wait for the next connection.
		// If it returns a temporary error then simply retry.
		// If it returns any other error then exit immediately.

View on GitHub (pinned to 7586a4d1bd)

Solutions

  1. Read the wrapped error; 'no such file' or 'failed to find any PEM data' means fix the CA path/content first.
  2. Validate the CA file: openssl x509 -in ca.pem -noout -text must parse and show a CA:TRUE basic constraint.
  3. Ensure the mutual-TLS flag combination matches the intent (client-cert required vs optional) per rqlite TLS docs.
  4. Restart the node after correcting the flags; confirm inter-node connectivity with the new config.

Example fix

// before
rqlited -node-tls-cert c.pem -node-tls-key k.pem -node-tls-ca /etc/rqlite/missing-ca.pem
// cannot create TLS config: open /etc/rqlite/missing-ca.pem: no such file or directory
// after
rqlited -node-tls-cert c.pem -node-tls-key k.pem -node-tls-ca /etc/rqlite/ca.pem
Defensive patterns

Strategy: validation

Validate before calling

caPEM, err := os.ReadFile(caCertPath)
if err != nil { return err }
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(caPEM) { return errors.New("invalid or empty CA bundle") }
if !bytes.Contains(caPEM, []byte("BEGIN CERTIFICATE")) { return errors.New("CA file is not PEM") }

Try / catch

mux, err := tcp.NewMutualTLSMux(ln, cert, key, caCert, verifyCN)
if err != nil && strings.Contains(err.Error(), "cannot create TLS config") {
    log.Fatalf("invalid TLS/CA configuration: %v", err)
}

Prevention

When it happens

Trigger: Starting rqlited with mutual TLS (-node-tls-ca / -node-verify-client / -node-no-verify-hostnames variants) where the CA cert file is missing, unreadable, or not a valid certificate; or invalid CA/mTLS-state combinations.

Common situations: CA bundle path typo; CA file empty or not PEM; combining -node-tls-ca with mismatched mutual-TLS flags; Kubernetes secret mounts missing the ca.crt key; expired root CA being rejected by strict parsing.

Understand the failure class

Related errors


AI-assisted analysis of rqlite/rqlite@7586a4d1bd (2026-09-03). Data as JSON: /api/errors/34b49c027b47c09a. Report an issue: GitHub.