caddyserver/caddy · error
expanding CA endpoint '%s': %v
Error message
expanding CA endpoint '%s': %v
What it means
Returned by ACMEIssuer.Provision (modules/caddytls/acmeissuer.go:147) when the ACME directory endpoint field contains a placeholder that fails to expand. Because the replacer treats unresolved placeholders as errors, an unknown env var or malformed expression in the acme_ca URL aborts provisioning of the issuer.
Source
Thrown at modules/caddytls/acmeissuer.go:147
func (iss *ACMEIssuer) Provision(ctx caddy.Context) error {
iss.logger = ctx.Logger()
repl := caddy.NewReplacer()
// expand email address, if non-empty
if iss.Email != "" {
email, err := repl.ReplaceOrErr(iss.Email, true, true)
if err != nil {
return fmt.Errorf("expanding email address '%s': %v", iss.Email, err)
}
iss.Email = email
}
// expand CA endpoint, if non-empty
if iss.CA != "" {
ca, err := repl.ReplaceOrErr(iss.CA, true, true)
if err != nil {
return fmt.Errorf("expanding CA endpoint '%s': %v", iss.CA, err)
}
iss.CA = ca
}
// expand TestCA endpoint, if non-empty
if iss.TestCA != "" {
testca, err := repl.ReplaceOrErr(iss.TestCA, true, true)
if err != nil {
return fmt.Errorf("expanding TestCA endpoint '%s': %v", iss.TestCA, err)
}
iss.TestCA = testca
}
// expand EAB credentials, if non-empty
if iss.ExternalAccount != nil {
if iss.ExternalAccount.KeyID != "" {
keyID, err := repl.ReplaceOrErr(iss.ExternalAccount.KeyID, true, true)
if err != nil {View on GitHub (pinned to 50e54ee279)
Solutions
- Export the variable in the service environment (Environment=ACME_DIRECTORY_URL=... in the unit, or env: in compose) and restart Caddy
- Correct the placeholder spelling/braces - it must be exactly {env.VAR}
- If the endpoint is fixed, write the literal URL (e.g. https://acme.example.com/directory) and remove the placeholder
- Validate config early: caddy validate --config Caddyfile
Example fix
# before
{
acme_ca {env.ACME_DIRECTORY} # variable name is ACME_DIRECTORY_URL
}
# after
{
acme_ca {env.ACME_DIRECTORY_URL}
}
# compose:
# environment:
# - ACME_DIRECTORY_URL=https://acme.internal/dir Defensive patterns
Strategy: validation
Validate before calling
// fail fast if the ACME directory env var is missing
if os.Getenv("ACME_DIRECTORY_URL") == "" && strings.Contains(cfgText, "{env.ACME_DIRECTORY_URL}") {
return errors.New("ACME_DIRECTORY_URL must be set before starting Caddy")
} Try / catch
if err := issuer.Provision(ctx); err != nil {
if strings.Contains(err.Error(), "expanding CA endpoint") {
// env var missing or placeholder typo: set it in the unit/compose and reload
}
return err
} Prevention
- Keep private ACME endpoints in one variable used by both CI and the service unit
- Use literal URLs for stable endpoints; reserve placeholders for values that truly vary per host
- Add caddy validate to the deploy pipeline run with the same env
When it happens
Trigger: Configuring acme_ca (or the JSON issuer's "ca") with a placeholder such as {env.ACME_DIRECTORY_URL} that resolves to nothing, or with invalid placeholder syntax; also custom shorthands not registered in the replacer.
Common situations: Pointing at a private ACME server (step-ca, smallstep, Vault) via env var that is missing in the systemd unit or container; renaming the variable in CI but not in the Caddy config; extra spaces inside braces like { env.URL }.
Related errors
- expanding email address '%s': %v
- expanding TestCA endpoint '%s': %v
- expanding EAB key ID '%s': %v
- expanding EAB MAC key (redacted): %v
- expanding account key PEM '%s': %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/d2c6668fa969905a.
Report an issue: GitHub.