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

  1. 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.
  2. If using a custom storage backend, restore connectivity/credentials and restart Caddy.
  3. Verify the storage object is valid PEM and not truncated; restore from backup or delete the CA assets to regenerate if corrupt.
  4. 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

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


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/1eebdfb136debb60. Report an issue: GitHub.