caddyserver/caddy · error
loading certificates: %v
Error message
loading certificates: %v
What it means
After all certificate loader modules are provisioned, each loader's LoadCertificates() is called to actually read the certificates from disk (or elsewhere). This error wraps a loader failure: files that do not exist, are unreadable due to permissions, or are not parseable as PEM-encoded certificate/key pairs.
Source
Thrown at modules/caddytls/tls.go:266
// manual/static (unmanaged) certificates - we do this in
// provision so that other apps (such as http) can know which
// certificates have been manually loaded, and also so that
// commands like validate can be a better test
certCacheMu.RLock()
magic := certmagic.New(certCache, certmagic.Config{
Storage: ctx.Storage(),
Logger: t.logger,
OnEvent: t.onEvent,
OCSP: certmagic.OCSPConfig{
DisableStapling: t.DisableOCSPStapling,
},
DisableStorageCheck: t.DisableStorageCheck,
})
certCacheMu.RUnlock()
for _, loader := range t.certificateLoaders {
certs, err := loader.LoadCertificates()
if err != nil {
return fmt.Errorf("loading certificates: %v", err)
}
for _, cert := range certs {
hash, err := magic.CacheUnmanagedTLSCertificate(ctx, cert.Certificate, cert.Tags)
if err != nil {
return fmt.Errorf("caching unmanaged certificate: %v", err)
}
t.loaded[hash] = ""
}
}
// on-demand permission module
if t.Automation != nil && t.Automation.OnDemand != nil && t.Automation.OnDemand.PermissionRaw != nil {
if t.Automation.OnDemand.Ask != "" {
return fmt.Errorf("on-demand TLS config conflict: both 'ask' endpoint and a 'permission' module are specified; 'ask' is deprecated, so use only the permission module")
}
val, err := ctx.LoadModule(t.Automation.OnDemand, "PermissionRaw")
if err != nil {
return fmt.Errorf("loading on-demand TLS permission module: %v", err)View on GitHub (pinned to 50e54ee279)
Solutions
- Read the wrapped error: it comes from the specific loader and usually names the offending file
- Verify paths exist and are readable by the Caddy process user (check mount points in containers)
- Validate PEM format: 'openssl x509 -in cert.pem -noout' and 'openssl pkey -in key.pem -noout' must succeed
- Confirm cert/key pair matches: compare modulus or use 'openssl x509 -noout -modulus' vs 'openssl rsa -noout -modulus'
Example fix
# before
load_files: certificate=/etc/cert/example.pem (file missing)
# after: point to the real path and verify
$ openssl x509 -in /etc/ssl/example.com/cert.pem -noout
{"load_files": [{"certificate": "/etc/ssl/example.com/cert.pem", "key": "/etc/ssl/example.com/key.pem"}]} Defensive patterns
Strategy: validation
Validate before calling
for f in /certs/cert.pem /certs/key.pem; do test -r "$f" || echo "unreadable: $f" openssl x509 -in "$f" -noout >/dev/null 2>&1 || openssl pkey -in "$f" -noout >/dev/null 2>&1 || echo "not PEM: $f" done
Prevention
- Check cert/key readability and PEM parseability in your deployment scripts before starting Caddy
- Mount certs into containers at fixed paths and reference those paths
When it happens
Trigger: load_files pointing to a certificate or key path that does not exist or has wrong ownership/mode; PEM blocks that are truncated, binary (DER instead of PEM), or whose key does not match the certificate; load_folders containing corrupt files.
Common situations: Cert files mounted into a container at a different path than configured; permissions changed after a system update; cert/key regenerated by another tool in DER format; copy-pasting certs introducing whitespace/truncation.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- no CERTIFICATE pem block found in %s
- two policies with same match criteria have conflicting cert
- finalizing automatic HTTPS: %v
- unable to add %s to trust pool: %v
- parsing certificate in %s: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/0ca723014ac6701e.
Report an issue: GitHub.