caddyserver/caddy · error

failed reading ca cert: %v

Error message

failed reading ca cert: %v

What it means

While building the http loader's TLS config, each file in tls.root_ca (RootCAPEMFiles) is read with os.ReadFile; any read failure (missing file, permission denied, path is a directory) is wrapped with this message. Note AppendCertsFromPEM's boolean result is not checked, so only the file-read step can error — a wrong-format-but-readable file fails later at handshake time instead.

Source

Thrown at caddyconfig/httploader.go:202

			// See https://github.com/securego/gosec/issues/1054#issuecomment-2072235199
			//nolint:gosec
			tlsConfig = &tls.Config{Certificates: certs}
		} else if hl.TLS.ClientCertificateFile != "" && hl.TLS.ClientCertificateKeyFile != "" {
			cert, err := tls.LoadX509KeyPair(hl.TLS.ClientCertificateFile, hl.TLS.ClientCertificateKeyFile)
			if err != nil {
				return nil, err
			}
			//nolint:gosec
			tlsConfig = &tls.Config{Certificates: []tls.Certificate{cert}}
		}

		// trusted server certs
		if len(hl.TLS.RootCAPEMFiles) > 0 {
			rootPool := x509.NewCertPool()
			for _, pemFile := range hl.TLS.RootCAPEMFiles {
				pemData, err := os.ReadFile(pemFile)
				if err != nil {
					return nil, fmt.Errorf("failed reading ca cert: %v", err)
				}
				rootPool.AppendCertsFromPEM(pemData)
			}
			if tlsConfig == nil {
				tlsConfig = new(tls.Config)
			}
			tlsConfig.RootCAs = rootPool
		}

		client.Transport = &http.Transport{TLSClientConfig: tlsConfig}
	}

	return client, nil
}

var _ caddy.ConfigLoader = (*HTTPLoader)(nil)

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Verify the path exists and is readable by the Caddy process: 'ls -l /path/ca.pem'.
  2. Fix the path or mount the file into the container at the configured location.
  3. Ensure permissions allow the runtime user to read the file.
  4. Confirm the file is PEM-formatted so the pool actually gets populated once readable.

Example fix

# before
http https://cfg.internal/config.json {
  tls {
    root_ca /etc/ssl/cfg-ca.crt   # not mounted
  }
}

# after
# docker run ... -v ./cfg-ca.crt:/etc/caddy/cfg-ca.pem ...
http https://cfg.internal/config.json {
  tls {
    root_ca /etc/caddy/cfg-ca.pem
  }
}
Defensive patterns

Strategy: validation

Validate before calling

for f in /etc/caddy/ca.pem; do test -r "$f" || echo "unreadable: $f"; done

Prevention

When it happens

Trigger: tls { root_ca /path/that/does/not/exist.pem }; the CA file present but unreadable due to permissions or container mount issues; a path pointing at a directory.

Common situations: Docker/Kubernetes mounts that changed and dropped the CA file; running Caddy under a user without read access; absolute paths from another host copied verbatim.

Related errors


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