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

  1. Export the variable in the service environment (Environment=ACME_DIRECTORY_URL=... in the unit, or env: in compose) and restart Caddy
  2. Correct the placeholder spelling/braces - it must be exactly {env.VAR}
  3. If the endpoint is fixed, write the literal URL (e.g. https://acme.example.com/directory) and remove the placeholder
  4. 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

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


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