caddyserver/caddy · critical

automate: managing %v: %v

Error message

automate: managing %v: %v

What it means

On TLS app Start(), all names registered via the 'automate' certificate loader (t.automateNames) are submitted to t.Manage for certificate issuance/renewal. If managing any of them fails — ACME challenges fail, issuers error, storage fails — Start aborts with this error, printing the whole automate name set.

Source

Thrown at modules/caddytls/tls.go:418

// Start activates the TLS module.
func (t *TLS) Start() error {
	// warn if on-demand TLS is enabled but no restrictions are in place
	if t.Automation.OnDemand == nil || (t.Automation.OnDemand.Ask == "" && t.Automation.OnDemand.permission == nil) {
		for _, ap := range t.Automation.Policies {
			if ap.OnDemand && ap.isWildcardOrDefault() {
				if c := t.logger.Check(zapcore.WarnLevel, "YOUR SERVER MAY BE VULNERABLE TO ABUSE: on-demand TLS is enabled, but no protections are in place"); c != nil {
					c.Write(zap.String("docs", "https://caddyserver.com/docs/automatic-https#on-demand-tls"))
				}
				break
			}
		}
	}

	// now that we are running, and all manual certificates have
	// been loaded, time to load the automated/managed certificates
	err := t.Manage(t.automateNames)
	if err != nil {
		return fmt.Errorf("automate: managing %v: %v", t.automateNames, err)
	}

	if t.EncryptedClientHello != nil {
		echLogger := t.logger.Named("ech")

		// publish ECH configs in the background; does not need to block
		// server startup, as it could take a while; then keep keys rotated
		go func() {
			// publish immediately first
			if err := t.publishECHConfigs(echLogger); err != nil {
				echLogger.Error("publication(s) failed", zap.Error(err))
			}

			// then every so often, rotate and publish if needed
			// (both of these functions only do something if needed)
			for {
				select {
				case <-time.After(1 * time.Hour):

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Read the wrapped per-name error — it distinguishes challenge failure, rate limit, storage, and DNS issues
  2. Ensure ports 80/443 are publicly reachable, or configure the dns challenge with a working dns provider
  3. Set an ACME email and use the Let's Encrypt staging CA ('acme_ca') while debugging to avoid rate limits
  4. For local/internal names, attach an explicit policy with the internal issuer instead of relying on automate defaults

Example fix

# before: public ACME for an internal-only name
{"certificates": {"automate": ["myservice.lan"]}}
# after
{"automation": {"policies": [{"subjects": ["myservice.lan"], "issuers": [{"module": "internal"}]}]}}
Defensive patterns

Strategy: retry

Validate before calling

# pre-flight: can a public CA reach us?
nc -z -w3 yourhost 80 && nc -z -w3 yourhost 443 || echo 'challenge ports unreachable'
dig +short yourhost A | grep -q . || echo 'no DNS record'

Try / catch

err := caddy.Start(cfg)
if err != nil && strings.Contains(err.Error(), "automate: managing") {
	log.Printf("cert management failed: %v", err)
	// server did not start; fix reachability/credentials and retry after backoff
}

Prevention

When it happens

Trigger: automate: ["example.com"] where port 80/443 are unreachable so HTTP/TLS-ALPN challenges fail; no ACME account email; DNS challenge provider misconfigured; firewall blocking Let's Encrypt validation; internal names without a qualifying policy. The wrapped error from certmagic contains per-name detail.

Common situations: First-run issuance on firewalled or NAT'd servers; rate limits from repeated test issuance; DNS not yet pointing at the server; clock skew breaking ACME; storage permission errors.

Related errors


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