{"record":{"id":"411c1a4b78f9249e","repo":"caddyserver/caddy","slug":"loading-root-key-v-411c1a","errorCode":null,"errorMessage":"loading root key: %v","messagePattern":"loading root key: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddypki/maintain.go","lineNumber":87,"sourceCode":"\tif ca.Root == nil {\n\t\tif ca.needsRenewal(ca.root) {\n\t\t\t// TODO: implement root renewal (use same key)\n\t\t\tlog.Warn(\"root certificate expiring soon (FIXME: ROOT RENEWAL NOT YET IMPLEMENTED)\",\n\t\t\t\tzap.Duration(\"time_remaining\", time.Until(ca.interChain[0].NotAfter)),\n\t\t\t)\n\t\t}\n\t}\n\n\t// only maintain the intermediate if it's not manually provided in the config\n\tif ca.Intermediate == nil {\n\t\tif ca.needsRenewal(ca.interChain[0]) {\n\t\t\tlog.Info(\"intermediate expires soon; renewing\",\n\t\t\t\tzap.Duration(\"time_remaining\", time.Until(ca.interChain[0].NotAfter)),\n\t\t\t)\n\n\t\t\trootCert, rootKey, err := ca.loadOrGenRoot()\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"loading root key: %v\", err)\n\t\t\t}\n\t\t\tinterCert, interKey, err := ca.genIntermediate(rootCert, rootKey)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"generating new certificate: %v\", err)\n\t\t\t}\n\t\t\tca.interChain, ca.interKey = []*x509.Certificate{interCert}, interKey\n\n\t\t\tlog.Info(\"renewed intermediate\",\n\t\t\t\tzap.Time(\"new_expiration\", ca.interChain[0].NotAfter),\n\t\t\t)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// needsRenewal reports whether the certificate is within its renewal window\n// (i.e. the fraction of lifetime remaining is less than or equal to RenewalWindowRatio).","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddypki/maintain.go#L69-L105","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before: root key unreadable (permissions)\nls -l /var/lib/caddy/pki/authorities/local/root.key   # -rw------- root root\n# systemd service runs as caddy\n\n# after: fix ownership, restart, let the maintenance loop retry\nchown -R caddy:caddy /var/lib/caddy/pki\nsystemctl restart caddy","handlingStrategy":"retry","validationCode":"// probe storage health and file access before relying on the maintenance loop\nfunc canReadRootKey(storageDir, caID string) error {\n\tp := filepath.Join(storageDir, \"pki\", \"authorities\", caID, \"root.key\")\n\tf, err := os.Open(p)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"root key unreadable: %w\", err)\n\t}\n\treturn f.Close()\n}","typeGuard":null,"tryCatchPattern":"// renewCertsForCA runs on every maintenance tick; log and let it retry,\n// but escalate after repeated failures\nif err := p.renewCertsForCA(ca); err != nil {\n    p.log.Error(\"renewing intermediate certificates\",\n        zap.Error(err), zap.String(\"ca\", ca.ID))\n    // do not crash; the ticker will retry next interval. Alert if it persists\n    // past the certificate's remaining lifetime.\n}","preventionTips":["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"],"tags":["pki","maintenance","renewal","storage","permissions"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}