This episode covers the plugin ecosystem: the Traefik Hub marketplace and community plugins, popular plugins such as GeoIP blocking and ModSecurity WAF, the Go plugin development architecture with plugin.toml, and installation with localPlugins and plugin versioning.

Traefik's built-in middlewares are complete, but the real world always has additional needs. Episode 29 covers plugins: custom middlewares that extend Traefik without changing the core. From ready-to-use community plugins to developing your own plugin in Go.
Traefik's plugin architecture is simple: each plugin is a Go module implementing the middleware interface. Traefik v3 loads plugins through the Yaegi interpreter — so plugins can be enabled without rebuilding the image, even though they are compiled to Go. Let us explore the plugin marketplace, how to install, and how to write them.
Plugins are distributed through Traefik Hub (formerly Traefik Pilot) — the official catalog that can be searched and installed directly. Besides that, many open-source plugins circulate on GitHub with the same installation pattern.
Some popular plugin categories:
Plugins are installed through static configuration. The simplest way is experimental.localPlugins, which loads code from a local directory:
experimental:
localPlugins:
geo-block:
moduleName: github.com/yourorg/geo-blockservices:
traefik:
image: traefik:v3
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./config/plugins:/plugins-local/src/github.com/yourorg/geo-blockPlugins from Traefik Hub are installed with a different pattern — via a Hub token and experimental.plugins configuration with a specific version. Once registered, a plugin is used like a regular middleware:
http:
middlewares:
geo-filter:
plugin:
geo-block:
allowedCountries:
- "ID"
- "SG"
routers:
app:
rule: "Host(`app.example.com`)"
entrypoints:
- web
service: app-svc
middlewares:
- geo-filterA plugin is a Go module with this structure:
geo-block/
├── go.mod
├── plugin.toml
├── geo_block.go
└── geo_block_test.goThe plugin core implements the http.Handler interface — receives a request, does the logic, then calls the next handler:
package geoblock
import "net/http"
type Config struct {
AllowedCountries []string `json:"allowedCountries"`
}
func New(ctx context.Context, next http.Handler, config Config, name string) (http.Handler, error) {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
if !isAllowed(req) {
rw.WriteHeader(http.StatusForbidden)
return
}
next.ServeHTTP(rw, req)
}), nil
}The New function receives the user-defined configuration and returns a handler that wraps next. As long as context and net/http are available, a plugin can do anything a middleware does.
The plugin.toml file is the metadata that makes Traefik recognize a plugin:
[plugin]
description = "Geo blocking by country"
version = "0.1.0"
[go]
version = "1.21"
[go.mod]
module = "github.com/yourorg/geo-block"description: a short description.version: the plugin version.go.mod.module: the Go module name — must match the moduleName in the Traefik configuration.latest; specify an explicit version so behavior is reproducible.Warning
Plugins run in the Traefik process — a panicking plugin can disrupt request processing. Limit the number of plugins, keep the code simple, and do a security review of plugins from unknown sources.
Key takeaways:
experimental.localPlugins and directory mounts.plugin.toml and the http.Handler interface.In episode 30 next — the final episode of this series — we will bring all the lessons together: production checklist & best practices, a comparison of Traefik v2 versus v3, common pitfalls, and a complete checklist for a production-ready Traefik deployment.