caddyserver/caddy · critical
saving root certificate: %v
Error message
saving root certificate: %v
What it means
genRoot persists the freshly generated root certificate with storage.Store at storageKeyRootCert. Any storage write failure — permissions, disk full, backend outage — is wrapped as 'saving root certificate'. The root exists in memory but not durably, so Caddy refuses to continue rather than risk a different root next start.
Source
Thrown at modules/caddypki/ca.go:331
}
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)
if err != nil {
return nil, nil, fmt.Errorf("encoding root certificate: %v", err)
}
err = ca.storage.Store(ca.ctx, ca.storageKeyRootCert(), rootCertPEM)
if err != nil {
return nil, nil, fmt.Errorf("saving root certificate: %v", err)
}
rootKeyPEM, err := certmagic.PEMEncodePrivateKey(rootKey)
if err != nil {
return nil, nil, fmt.Errorf("encoding root key: %v", err)
}
err = ca.storage.Store(ca.ctx, ca.storageKeyRootKey(), rootKeyPEM)
if err != nil {
return nil, nil, fmt.Errorf("saving root key: %v", err)
}
return rootCert, rootKey, nil
}
func (ca CA) loadOrGenIntermediate(rootCert *x509.Certificate, rootKey crypto.Signer) (interCertChain []*x509.Certificate, interKey crypto.Signer, err error) {
var interCert *x509.Certificate
interCertPEM, err := ca.storage.Load(ca.ctx, ca.storageKeyIntermediateCert())
if err != nil {
if !errors.Is(err, fs.ErrNotExist) {View on GitHub (pinned to 50e54ee279)
Solutions
- Grant write access to the storage location for the Caddy process (chown/chmod the data dir or bucket prefix) and restart.
- Free disk space / fix backend quotas, then delete any partially written assets and restart so generation re-runs cleanly.
- If using custom storage, test it directly (caddy storage export/import) to confirm read/write works.
- Point storage to a known-writable location via the storage global option.
Defensive patterns
Strategy: retry
Validate before calling
// Pre-start: storage must be writable
probe := filepath.Join(dataDir, ".write-probe")
if err := os.WriteFile(probe, []byte("x"), 0o600); err != nil { log.Fatalf("storage not writable: %v", err) }
os.Remove(probe) Try / catch
if strings.Contains(err.Error(), "saving root certificate") {
// fix writability/space, remove partial assets, retry provisioning
} Prevention
- Health-check storage writability before first provisioning.
- Size disk quotas with PKI assets and logs in mind.
- For object-storage backends, verify PutObject policy in pre-deploy checks.
When it happens
Trigger: storage.Store fails: read-only or full filesystem for the data directory, custom storage backend (Redis/S3/consul) rejecting the write, or ownership mismatch on the storage tree. Runs only when a new root was just generated (first boot of a CA id).
Common situations: Docker bind-mount data dirs owned by root while Caddy runs as another user; disk-full nodes; S3 bucket policy denying PutObject; first provisioning racing a storage maintenance window.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- loading root cert: %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/8954e72ec258607b.
Report an issue: GitHub.