This episode covers Caddy's extensibility: the module system, popular plugins like caddy-dns and caddy-rate-limit, custom builds with xcaddy, and the basics of plugin development in Go.

Episode 2 introduced Caddy's modular architecture. Episode 29 puts it to full use: plugins. Nearly every Caddy feature is a module that can be replaced or added, and the community has built hundreds of plugins for DNS, rate limiting, authentication, and more.
You'll learn Caddy's module system, popular plugins worth knowing, how to build a custom binary with xcaddy, and a basic overview of plugin development in Go for those interested in contributing.
This is the episode that makes Caddy infinitely customizable — from a simple reverse proxy to a highly specialized edge proxy.
Caddy modules fall into categories:
caddy list-modules shows all modules available in your binary:
caddy list-modulesEvery module has a type and name, e.g. http.handlers.reverse_proxy. This helps you understand the structure when writing JSON configuration.
Plugins implement specific interfaces:
http.handlers.*).http.matchers.*).dns.providers.*).Plugins are registered at compile time. Adding a plugin means rebuilding the Caddy binary — which is where xcaddy comes in.
Some of the most used plugins:
We already saw the rate limit plugin example in episode 19. The common theme: adding capabilities that don't exist in the default build.
Before adding a plugin:
A plugin is code running at the edge — choose carefully.
xcaddy is the official tool for building Caddy with plugins:
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latestxcaddy requires Go to be installed. go install ...@latest places the xcaddy binary in $GOBIN.
The basic command:
xcaddy build v2.8.0 \
--with github.com/caddy-dns/cloudflarexcaddy build v2.8.0 builds a specific Caddy version with the given plugin. If no version is written, the latest is used.
Combine several plugins and pin their versions:
xcaddy build \
--with github.com/caddy-dns/cloudflare \
--with github.com/mholt/caddy-l4 \
--with github.com/caddyserver/caddy/v2@v2.8.0--with module@version allows pinning module versions. Store this build command in your documentation or a script — builds must be reproducible.
After the build finishes, a binary named caddy sits in the working directory. Verify:
./caddy list-modules | grep dns.providers./caddy list-modules | grep dns.providers confirms the DNS module was compiled in. Replace your system binary with this build and run it as usual.
To write your own plugin, you create a Go module that implements Caddy's interfaces:
package main
import "github.com/caddyserver/caddy/v2"
type myHandler struct{}
func (m *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) error {
// handler logic
return nil
}
func init() {
caddy.RegisterModule(myHandler{})
}
func main() {
caddycmd.Main()
}A plugin is registered via init() with caddy.RegisterModule. The full structure includes the CaddyModule, Provision, and Validate interfaces.
A good handler plugin also supports the Caddyfile:
func (m *myHandler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
if !d.NextArg() {
return d.ArgErr()
}
m.option = d.Val()
}
return nil
}UnmarshalCaddyfile reads arguments from the Caddyfile. With this method, your plugin can be used through a regular Caddyfile, not just JSON.
xcaddy, with --with pointing at your repository.caddy run and caddy list-modules.Episode 29 opened up Caddy's extensibility: the module system with core, standard, and third-party categories, popular plugins like caddy-dns and caddy-rate-limit, custom builds with xcaddy, and the basics of Go plugin development with registration and Caddyfile unmarshal.
Key takeaways:
caddy list-modules shows the compiled modules.xcaddy build --with module creates a custom binary.RegisterModule.list-modules | grep when done.In the next episode, episode 30 — the final episode of the series — we'll cover production deployment & best practices — a pre-production checklist, Caddyfile best practices, security hardening, performance tuning, operations with graceful reloads, high availability, common pitfalls, troubleshooting, and migrating from NGINX.