caddyserver/caddy · error

cannot enable remote admin without a certificate cache…

Error message

cannot enable remote admin without a certificate cache; configure identity management to initialize a certificate cache

What it means

Enabling admin.remote requires server identity management because the remote endpoint's TLS certificate comes from the identity certmagic cache (identityCertCache). If that package-level cache is nil — i.e. admin.identity did not run and configure a certificate cache — remote admin cannot be set up and startup fails with this error.

Solutions

  1. Add an admin.identity section to the same config (at minimum identifiers, letting the default ACME or internal issuer manage a certificate)
  2. Consider the 'internal' issuer for identity to avoid external ACME dependencies: {"module": "internal"}
  3. Verify the whole admin config with caddy validate before restart
  4. If you don't need remote admin, remove the admin.remote block

Example fix

// before
"admin": {
  "remote": { "listen": ":2021", "access_control": [...] }
}

// after
"admin": {
  "identity": {
    "identifiers": ["admin.example.com"],
    "issuers": [{"module": "internal"}]
  },
  "remote": { "listen": ":2021", "access_control": [...] }
}
Defensive patterns

Strategy: validation

Validate before calling

func remoteAdminConfigComplete(cfg *Config) error {
	if cfg.Admin != nil && cfg.Admin.Remote != nil {
		if cfg.Admin.Identity == nil || len(cfg.Admin.Identity.Identifiers) == 0 {
			return errors.New("admin.remote requires admin.identity with identifiers")
		}
	}
	return nil
}

Prevention

When it happens

Trigger: JSON config with admin.remote configured but no admin.identity section (or identity provisioning skipped/failed silently in this path); ordering issues where remote setup runs before identity setup in the same config load.

Common situations: Experimenting with remote admin and adding only the remote block; copying partial examples from docs; identity issuers left empty in a config that still requests remote access.

Understand the failure class

Related errors


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

Appendix: source

Thrown at admin.go:561

	}

	// create client certificate pool for TLS mutual auth, and extract public keys
	// so that we can enforce access controls at the application layer
	clientCertPool := x509.NewCertPool()
	for i, accessControl := range cfg.Admin.Remote.AccessControl {
		for j, certBase64 := range accessControl.PublicKeys {
			cert, err := decodeBase64DERCert(certBase64)
			if err != nil {
				return fmt.Errorf("access control %d public key %d: parsing base64 certificate DER: %v", i, j, err)
			}
			accessControl.publicKeys = append(accessControl.publicKeys, cert.PublicKey)
			clientCertPool.AddCert(cert)
		}
	}

	// create TLS config that will enforce mutual authentication
	if identityCertCache == nil {
		return fmt.Errorf("cannot enable remote admin without a certificate cache; configure identity management to initialize a certificate cache")
	}
	cmCfg := cfg.Admin.Identity.certmagicConfig(remoteLogger, false)
	tlsConfig := cmCfg.TLSConfig()
	tlsConfig.NextProtos = nil // this server does not solve ACME challenges
	tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
	tlsConfig.ClientCAs = clientCertPool

	// convert logger to stdlib so it can be used by HTTP server
	serverLogger, err := zap.NewStdLogAt(remoteLogger, zap.DebugLevel)
	if err != nil {
		return err
	}

	serverMu.Lock()
	// create secure HTTP server
	remoteAdminServer = &http.Server{
		Addr:              addr.String(), // for logging purposes only
		Handler:           handler,

View on GitHub (pinned to 50e54ee279)