caddyserver/caddy · error
loading root cert: %v
Error message
loading root cert: %v
What it means
CA.loadOrGenRoot reads the root certificate PEM from Caddy storage (default path like storage/caddy/pki/<id>/ca/root.crt). If storage.Load fails with any error other than fs.ErrNotExist — permission denied, backend down, corrupt index — the error is wrapped as 'loading root cert'. A clean not-found instead triggers root generation, so this error means storage exists but is failing.
Source
Thrown at modules/caddypki/ca.go:288
if err != nil {
return nil, fmt.Errorf("initializing certificate authority: %v", err)
}
return auth, nil
}
func (ca CA) loadOrGenRoot() (rootCert *x509.Certificate, rootKey crypto.Signer, err error) {
if ca.Root != nil {
rootChain, rootSigner, err := ca.Root.Load()
if err != nil {
return nil, nil, err
}
return rootChain[0], rootSigner, nil
}
rootCertPEM, err := ca.storage.Load(ca.ctx, ca.storageKeyRootCert())
if err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return nil, nil, fmt.Errorf("loading root cert: %v", err)
}
// 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 {View on GitHub (pinned to 50e54ee279)
Solutions
- Fix storage access: chown/chmod the data storage directory (or bucket/prefix) so the Caddy process can read the CA assets; verify with sudo -u caddy cat <storage>/caddy/pki/.../root.crt.
- If using a custom storage backend, restore connectivity/credentials and restart Caddy.
- Verify the storage object is valid PEM and not truncated; restore from backup or delete the CA assets to regenerate if corrupt.
- Set the storage path explicitly (storage file_system <dir>) to a writable location instead of relying on default directory resolution.
Defensive patterns
Strategy: retry
Validate before calling
// Pre-start: can the process actually read the CA root cert?
if _, err := os.ReadFile(filepath.Join(dataDir, "caddy", "pki", caID, "ca", "root.crt")); err != nil && !errors.Is(err, fs.ErrNotExist) {
log.Fatalf("storage unreadable: %v", err)
} Try / catch
// storage hiccups are often transient; retry once after a delay, else fail loudly
if err := startCaddy(cfg); err != nil {
if strings.Contains(err.Error(), "loading root cert") {
time.Sleep(2 * time.Second)
if err2 := startCaddy(cfg); err2 != nil { return err2 }
return nil
}
return err
} Prevention
- Give the Caddy process ownership of its data directory from day one.
- Probe custom storage backends (a cheap read) in container healthchecks before Caddy starts.
- Mount persistent, writable storage explicitly in containers rather than defaulting to ephemeral paths.
When it happens
Trigger: storage.Load(ca.storageKeyRootCert()) returns a non-ErrNotExist error: unreadable file (mode/ownership), a failing custom storage module (Redis/S3/consul unreachable), or a filesystem I/O error on the default data directory. Raised during pki app provisioning.
Common situations: Running Caddy as a service whose data directory is owned by root while the process runs unprivileged; a custom storage backend outage at startup; read-only filesystem containers; NFS/overlay permission quirks.
Related errors
- saving root certificate: %v
- loading intermediate cert: %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/1eebdfb136debb60.
Report an issue: GitHub.