caddyserver/caddy · critical
loading root key: %v
Error message
loading root key: %v
What it means
After successfully loading a root cert from storage, Caddy loads the root's private key (storageKeyRootKey, e.g. key.pem). Any storage error here — the file/block is missing in a way not surfaced as fs.ErrNotExist handling for this branch, or the backend fails — is wrapped as 'loading root key'. Without the key the root cannot sign a new intermediate.
Source
Thrown at modules/caddypki/ca.go:307
}
// TODO: should we require that all or none of the assets are required before overwriting anything?
rootCert, rootKey, err = ca.genRoot()
if err != nil {
return nil, nil, fmt.Errorf("generating root: %v", err)
}
}
if rootCert == nil {
rootCert, err = pemDecodeCertificate(rootCertPEM)
if err != nil {
return nil, nil, fmt.Errorf("parsing root certificate PEM: %v", err)
}
}
if rootKey == nil {
rootKeyPEM, err := ca.storage.Load(ca.ctx, ca.storageKeyRootKey())
if err != nil {
return nil, nil, fmt.Errorf("loading root key: %v", err)
}
rootKey, err = certmagic.PEMDecodePrivateKey(rootKeyPEM)
if err != nil {
return nil, nil, fmt.Errorf("decoding root key: %v", err)
}
}
return rootCert, rootKey, nil
}
func (ca CA) genRoot() (rootCert *x509.Certificate, rootKey crypto.Signer, err error) {
repl := ca.newReplacer()
rootCert, rootKey, err = generateRoot(repl.ReplaceAll(ca.RootCommonName, ""))
if err != nil {
return nil, nil, fmt.Errorf("generating CA root: %v", err)
}
rootCertPEM, err := pemEncodeCert(rootCert.Raw)View on GitHub (pinned to 50e54ee279)
Solutions
- Restore the root key object at the expected storage key (pair it with the existing root.crt), e.g. copy key.pem back to storage/caddy/pki/<id>/ca/.
- If the key is lost, the root is unusable: delete that CA's storage assets and let Caddy generate a new root+intermediate, then redistribute trust (caddy trust) and reissue leaf certs (they are typically short-lived).
- Fix permissions on the storage directory so the process can read both CA files.
- For multi-instance deployments, verify all nodes see the same, complete storage contents.
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-start integrity check: cert present => key must be present too
_, certErr := os.Stat(caDir + "/root.crt")
_, keyErr := os.Stat(caDir + "/key.pem")
if certErr == nil && keyErr != nil { log.Fatal("partial CA state: root cert without key") } Type guard
func caAssetsComplete(dir string) bool {
for _, f := range []string{"root.crt", "key.pem"} {
if _, err := os.Stat(filepath.Join(dir, f)); err != nil { return false }
}
return true
} Try / catch
if err != nil && strings.Contains(err.Error(), "loading root key") {
// decide: restore key from backup, or wipe CA dir to regenerate + re-trust
}
return err Prevention
- Back up CA assets as an all-or-none set.
- Exclude CA storage from ad-hoc security cleanup scripts that delete key files.
- After any restore, run an asset-completeness check before starting Caddy.
When it happens
Trigger: storage.Load for the root key returns an error: the key object was deleted while the cert remains (partial state), permission/ownership mismatch on key.pem, or a custom storage backend error. Happens during loadOrGenRoot after the cert loaded fine.
Common situations: Someone removed key.pem 'for security' leaving root.crt; backup/restore that skipped dot/PEM key files; storage backend partial outage returning errors for one object; multi-node setups with inconsistent replicated storage.
Related errors
- saving root key: %v
- loading signing key: %v
- loading intermediate key: %v
- failed to get root and intermediate cert for CA %s: %v
- failed to provision CA %s, %w
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/01414076428f7c5d.
Report an issue: GitHub.