caddyserver/caddy · error
pki_root CA pool requires that a PKI app is configured: %v
Error message
pki_root CA pool requires that a PKI app is configured: %v
What it means
The `tls.ca_pool.source.pki_root` module's Provision() calls ctx.AppIfConfigured("pki"), which errors when the PKI app is not present in the effective Caddy configuration. The pki_root trust pool reads root certificates from Caddy's internal PKI app, so that app must be configured (usually via the `pki` global option or any `internal` issuer usage) before this pool can be provisioned.
Source
Thrown at modules/caddytls/capools.go:235
pool *x509.CertPool
certs []*x509.Certificate
}
// CaddyModule implements caddy.Module.
func (PKIRootCAPool) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "tls.ca_pool.source.pki_root",
New: func() caddy.Module {
return new(PKIRootCAPool)
},
}
}
// Loads the PKI app and load the root certificates into the certificate pool
func (p *PKIRootCAPool) Provision(ctx caddy.Context) error {
pkiApp, err := ctx.AppIfConfigured("pki")
if err != nil {
return fmt.Errorf("pki_root CA pool requires that a PKI app is configured: %v", err)
}
pki := pkiApp.(*caddypki.PKI)
for _, caID := range p.Authority {
c, err := pki.GetCA(ctx, caID)
if err != nil || c == nil {
return fmt.Errorf("getting CA %s: %v", caID, err)
}
p.ca = append(p.ca, c)
}
caPool := x509.NewCertPool()
var certs []*x509.Certificate
for _, ca := range p.ca {
rootCert := ca.RootCertificate()
if rootCert == nil {
return fmt.Errorf("CA %s has no root certificate", ca.ID)
}
caPool.AddCert(rootCert)View on GitHub (pinned to 50e54ee279)
Solutions
- Add a `pki` global option block (or a site/issuer that instantiates the PKI app) to the config, e.g. `{ pki { ca local { name "My Local CA" } } }`.
- If you do not want Caddy's internal PKI, switch the trust pool to `file`, `inline`, `storage`, or `system` instead of `pki_root`.
- Verify the config JSON contains a top-level `"apps"."pki"` object before applying (`caddy adapt` then inspect).
Example fix
# before
example.com {
tls {
client_auth {
trust_pool pki_root
}
}
}
# after
{
pki {
ca local {
name "Local CA"
}
}
}
example.com {
tls {
client_auth {
trust_pool pki_root
}
}
} Defensive patterns
Strategy: validation
Validate before calling
// before applying config, confirm the pki app is present
import "encoding/json"
func hasPKIApp(cfgJSON []byte) bool {
var cfg struct {
Apps map[string]json.RawMessage `json:"apps"`
}
if err := json.Unmarshal(cfgJSON, &cfg); err != nil {
return false
}
_, ok := cfg.Apps["pki"]
return ok
} Prevention
- Keep the pki global option and any pki_root/pki_intermediate trust pools in the same config source so they are applied atomically.
- Run `caddy validate --config Caddyfile --adapter caddyfile` in CI to catch missing-app errors before deploy.
- When splitting configs into snippets, grep for pki_root/pki_intermediate usages and ensure a pki app is still adapted.
When it happens
Trigger: Configuring `trust_pool pki_root` (with one or more `ca <id>` entries) in a Caddyfile or JSON config where no PKI app block exists — e.g. no `pki` global option and no site using the `internal` issuer, or the pki app block was removed/renamed while the trust pool remained.
Common situations: Copying a client-auth snippet that references pki_root into a minimal test config; upgrading/restructuring a config where the internal CA setup was dropped; using a named CA (e.g. `ca custom`) that was never declared in the pki app.
Related errors
- pki_intermediate CA pool requires that a PKI app is configur
- getting CA %s: %v
- CA %s has no root certificate
- --input is required
- setting up subroutes: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/364edb6059a91fda.
Report an issue: GitHub.