caddyserver/caddy · error
loading root key: %v
Error message
loading root key: %v
What it means
Emitted by renewCertsForCA (modules/caddypki/maintain.go:87) during the PKI maintenance loop when an intermediate certificate is nearing expiry and loadOrGenRoot fails to load or generate the root key. The wrapped error carries the real cause: an unreadable/corrupt root key file, a PEM decode failure, or a storage backend error.
Source
Thrown at modules/caddypki/maintain.go:87
if ca.Root == nil {
if ca.needsRenewal(ca.root) {
// TODO: implement root renewal (use same key)
log.Warn("root certificate expiring soon (FIXME: ROOT RENEWAL NOT YET IMPLEMENTED)",
zap.Duration("time_remaining", time.Until(ca.interChain[0].NotAfter)),
)
}
}
// only maintain the intermediate if it's not manually provided in the config
if ca.Intermediate == nil {
if ca.needsRenewal(ca.interChain[0]) {
log.Info("intermediate expires soon; renewing",
zap.Duration("time_remaining", time.Until(ca.interChain[0].NotAfter)),
)
rootCert, rootKey, err := ca.loadOrGenRoot()
if err != nil {
return fmt.Errorf("loading root key: %v", err)
}
interCert, interKey, err := ca.genIntermediate(rootCert, rootKey)
if err != nil {
return fmt.Errorf("generating new certificate: %v", err)
}
ca.interChain, ca.interKey = []*x509.Certificate{interCert}, interKey
log.Info("renewed intermediate",
zap.Time("new_expiration", ca.interChain[0].NotAfter),
)
}
}
return nil
}
// needsRenewal reports whether the certificate is within its renewal window
// (i.e. the fraction of lifetime remaining is less than or equal to RenewalWindowRatio).View on GitHub (pinned to 50e54ee279)
Solutions
- Inspect the wrapped error for the root cause: fix file permissions (chown/chmod) on the root key under <storage>/pki/authorities/<id>/root.key
- If the root key was lost, delete the CA's storage subtree (root + intermediate) so Caddy generates a fresh root and intermediate, and re-install the new root CA in client trust stores
- Verify storage backend health: run caddy start with local storage or check the Redis/S3 plugin connectivity
- Restore the exact root key from a backup if clients already trust the root, then restart Caddy so the maintenance loop retries
Example fix
# before: root key unreadable (permissions) ls -l /var/lib/caddy/pki/authorities/local/root.key # -rw------- root root # systemd service runs as caddy # after: fix ownership, restart, let the maintenance loop retry chown -R caddy:caddy /var/lib/caddy/pki systemctl restart caddy
Defensive patterns
Strategy: retry
Validate before calling
// probe storage health and file access before relying on the maintenance loop
func canReadRootKey(storageDir, caID string) error {
p := filepath.Join(storageDir, "pki", "authorities", caID, "root.key")
f, err := os.Open(p)
if err != nil {
return fmt.Errorf("root key unreadable: %w", err)
}
return f.Close()
} Try / catch
// renewCertsForCA runs on every maintenance tick; log and let it retry,
// but escalate after repeated failures
if err := p.renewCertsForCA(ca); err != nil {
p.log.Error("renewing intermediate certificates",
zap.Error(err), zap.String("ca", ca.ID))
// do not crash; the ticker will retry next interval. Alert if it persists
// past the certificate's remaining lifetime.
} Prevention
- Run Caddy under a service unit with a dedicated writable StateDirectory (systemd StateDirectory=caddy)
- Back up <storage>/pki/authorities/<id>/ (root.key + root.crt) whenever you back up Caddy config
- Monitor log level=ERROR containing 'loading root key' or 'renewing intermediate' as an alert
- Test container images by running as the service user and touching the storage dir before deploy
When it happens
Trigger: The intermediate needs renewal (ca.needsRenewal true) and ca.loadOrGenRoot() errors: root key missing from the data directory, wrong permissions on the storage folder, corrupted PEM file, key/cert mismatch in a manually-formatted keystore, or a failing distributed storage plugin (Redis, S3, etc.) used as Caddy storage.
Common situations: Storage directory deleted or partially restored from backup (root cert present, key gone); containers running as a user without read access to /data/caddy/pki; disk-full events truncating key files; a flaky external storage backend; SELinux denying reads.
Related errors
- generating new certificate: %v
- making folder for CA database: %v
- failed to get root and intermediate cert for CA %s: %v
- failed to provision CA %s, %w
- loading storage module: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/411c1a4b78f9249e.
Report an issue: GitHub.