caddyserver/caddy · error

cannot enable remote admin without a certificate cache; conf

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.

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)

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.