Skip to main content
Caddy’s module system is the foundation of its extensibility. Every component in Caddy—from HTTP handlers to TLS certificate issuers—is implemented as a module. This architecture allows you to extend Caddy with custom functionality or use third-party modules.

What is a Module?

A module is any Go type that implements the Module interface:
modules.go:54-60

Module Information

Each module must provide metadata about itself:
modules.go:62-77

Module Namespaces

Module IDs follow a hierarchical naming convention:
modules.go:79-98
Namespace structure: <namespace>.<name>Top-level modules (apps) have no namespace, just a name like http or tls.

Common Namespaces

Module Registration

Modules must be registered before Caddy can use them:
modules.go:130-161
Module registration typically happens in init() functions and will panic if:
  • The module ID is empty or reserved
  • The constructor function is nil
  • The module is already registered

Example Registration

Module Lifecycle

When a module is loaded, it goes through several phases:
1

Instantiation

Caddy calls ModuleInfo.New() to create a new instance:
context.go:369
2

Unmarshaling

The module’s configuration is unmarshaled into the instance:
context.go:382-387
3

Provisioning

If the module implements Provisioner, its Provision() method is called:
modules.go:288-298
context.go:418-430
4

Validation

If the module implements Validator, its Validate() method is called:
modules.go:300-307
context.go:433-444
5

Usage

The module is now ready to be used. It’s typically type-asserted to a specific interface expected by the host module.
6

Cleanup

When the config is unloaded, if the module implements CleanerUpper, its Cleanup() method is called:
modules.go:309-317
context.go:75-83

Loading Modules

Caddy provides the LoadModule method to load modules from configuration:
context.go:181

Supported Field Types

The LoadModule method supports several raw module types:
For a single module:
For a list of modules:
For a map where keys are module names:

Struct Tags

Modules are configured using struct tags:
modules.go:319-336
Required tags:
  • namespace - The module namespace to search (e.g., http.handlers)
Optional tags:
  • inline_key - The JSON key containing the module name (e.g., handler)
When using ModuleMap, the map key IS the module name, so inline_key is not needed.

Creating Custom Modules

Here’s a complete example of a custom HTTP handler module:
1

Define the Module

2

Implement Provisioner

3

Implement Validator

4

Implement Handler Interface

5

Add Interface Guards

Module Discovery

Caddy provides functions to discover registered modules:
modules.go:195-242
Examples:

Best Practices

1

Always Use Pointers

Module constructors should return pointers:
2

Validate Configuration

Implement Validator to catch configuration errors early:
3

Clean Up Resources

Implement CleanerUpper if your module allocates resources:
4

Use Context Logger

Get a properly-configured logger from the context:
5

Add Interface Guards

Use compile-time interface guards to catch mistakes:
Common Pitfalls:
  • Forgetting to register the module in init()
  • Not returning pointers from constructors
  • Performing I/O in Provision() that should be in Start()
  • Not cleaning up resources in Cleanup()