caddyserver/caddy · error

provisioning automation policy

Error message

provisioning automation policy %d: %v

What it means

Each user-defined policy in tls.automation.policies is provisioned in order; this error wraps the failure of policy at index %d. The wrapped message comes from AutomationPolicy.Provision (automation.go:182) and can be: subject punycode/IDNA conversion failure, storage module load failure, external certificate manager (ManagersRaw) load failure, issuer module load failure, or default issuer provisioning failure.

Solutions

  1. The wrapped error names the exact sub-failure; fix that first (it is usually more specific than this message)
  2. The index %d refers to the position in automation.policies — count from 0 to find the offending policy
  3. Validate subjects are legal DNS names (use punycode for unicode, avoid underscores)
  4. Run 'caddy validate' — provisioning errors surface at validation time, before Start

Example fix

// before
{"policies": [{"subjects": ["exa_mple.com"], "issuers": [{"module": "acme"}]}]}
// after
{"policies": [{"subjects": ["example.com"], "issuers": [{"module": "acme"}]}]}
Defensive patterns

Strategy: validation

Validate before calling

import "github.com/miekg/dns"

func validSubject(s string) bool {
	_, ok := dns.IsDomainName(s)
	return ok && !strings.Contains(s, "_")
}

Prevention

When it happens

Trigger: A subject like 'exa_mple.com' or invalid unicode that fails idna.ToASCII; an issuers entry with a bad module name or invalid fields (e.g. acme issuer with malformed ca URL); a managers entry whose plugin is not compiled in; policy storage module misconfiguration.

Common situations: Hand-written policy subjects with underscores or wildcards in the wrong position; typo'd issuer modules; plugin version drift after Caddy upgrades; DN-ish names or non-DNS strings in subjects.

Related errors


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

Appendix: source

Thrown at modules/caddytls/tls.go:317

		// if any names specified by the "automate" loader do not qualify for a public
		// certificate, we should initialize a default internal automation policy
		// (but we don't want to do this unnecessarily, since it may prompt for password!)
		if certmagic.SubjectQualifiesForPublicCert(n) {
			continue
		}
		t.Automation.defaultInternalAutomationPolicy = &AutomationPolicy{
			IssuersRaw: []json.RawMessage{json.RawMessage(`{"module":"internal"}`)},
		}
		err = t.Automation.defaultInternalAutomationPolicy.Provision(t)
		if err != nil {
			return fmt.Errorf("provisioning default internal automation policy: %v", err)
		}
		break
	}
	for i, ap := range t.Automation.Policies {
		err := ap.Provision(t)
		if err != nil {
			return fmt.Errorf("provisioning automation policy %d: %v", i, err)
		}
	}

	// run replacer on ask URL (for environment variables) -- return errors to prevent surprises (#5036)
	if t.Automation != nil && t.Automation.OnDemand != nil && t.Automation.OnDemand.Ask != "" {
		t.Automation.OnDemand.Ask, err = repl.ReplaceOrErr(t.Automation.OnDemand.Ask, true, true)
		if err != nil {
			return fmt.Errorf("preparing 'ask' endpoint: %v", err)
		}
		perm := PermissionByHTTP{
			Endpoint: t.Automation.OnDemand.Ask,
		}
		if err := perm.Provision(ctx); err != nil {
			return fmt.Errorf("provisioning 'ask' module: %v", err)
		}
		t.Automation.OnDemand.permission = perm
	}

View on GitHub (pinned to 50e54ee279)