caddyserver/caddy · error

loading TLS storage module: %v

Error message

loading TLS storage module: %v

What it means

The automation policy declares a storage module (StorageRaw, e.g. a custom certmagic storage backend) and caddy.Context.LoadModule failed to instantiate/provision it. The wrapped error carries the underlying module's own failure (bad module config, unknown module name, or the module's Provision error).

Source

Thrown at modules/caddytls/automation.go:200

func (ap *AutomationPolicy) Provision(tlsApp *TLS) error {
	// replace placeholders in subjects to allow environment variables
	repl := caddy.NewReplacer()
	subjects := make([]string, len(ap.SubjectsRaw))
	for i, sub := range ap.SubjectsRaw {
		sub = repl.ReplaceAll(sub, "")
		subASCII, err := idna.ToASCII(sub)
		if err != nil {
			return fmt.Errorf("could not convert automation policy subject '%s' to punycode: %v", sub, err)
		}
		subjects[i] = subASCII
	}
	ap.subjects = subjects

	// policy-specific storage implementation
	if ap.StorageRaw != nil {
		val, err := tlsApp.ctx.LoadModule(ap, "StorageRaw")
		if err != nil {
			return fmt.Errorf("loading TLS storage module: %v", err)
		}
		cmStorage, err := val.(caddy.StorageConverter).CertMagicStorage()
		if err != nil {
			return fmt.Errorf("creating TLS storage configuration: %v", err)
		}
		ap.storage = cmStorage
	}

	// we don't store loaded modules directly in the certmagic config since
	// policy provisioning may happen more than once (during auto-HTTPS) and
	// loading a module clears its config bytes; thus, load the module and
	// store them on the policy before putting it on the config

	// load and provision any cert manager modules
	if ap.ManagersRaw != nil {
		ap.hadExplicitManagers = true
		vals, err := tlsApp.ctx.LoadModule(ap, "ManagersRaw")
		if err != nil {

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Read the wrapped error — it identifies which module failed and why; fix that module's configuration.
  2. If the module is unknown, build Caddy (xcaddy build --with ...) including the storage plugin, or remove the storage block to use default local storage.
  3. Verify credentials, endpoints, and permissions for the storage backend (e.g. S3 bucket write access).
  4. Test the storage config in isolation with the module's own tooling before wiring it into the automation policy.

Example fix

// before: plugin not compiled in
{"apps":{"tls":{"automation":{"policies":[{"subjects":["example.com"],"storage":{"module":"redis"}}]}}}}

// after: build with plugin, or use file_system storage
"storage": {"module": "file_system", "root": "/var/lib/caddy/storage"}
Defensive patterns

Strategy: try-catch

Validate before calling

// Confirm the module is present before referencing it.
if caddy.GetModule("caddy.storage.redis") == nil { // pseudocode; iterate caddy.Modules()
    return errors.New("storage plugin not compiled in; rebuild with xcaddy --with")
}

Try / catch

if err := policy.Provision(tlsApp); err != nil {
    if strings.Contains(err.Error(), "loading TLS storage module") {
        return fmt.Errorf("fix or remove policy storage config: %w", err)
    }
}

Prevention

When it happens

Trigger: Setting "storage" inside a TLS automation policy to a module name that is not compiled into the binary; a storage module whose Provision rejects its options (bad connection string, unwritable path); JSON type mismatches in the storage module config.

Common situations: Using a plugin storage backend (S3, Redis, Consul...) without having built Caddy with that plugin; typo'd module names in JSON config; wrong credentials/endpoints for the storage backend; version drift between plugin and Caddy.

Understand the failure class

Related errors


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