caddyserver/caddy
Context
caddy.Context is the runtime environment passed to every module's Provision method. Defined in context.go (~25 KB).
Purpose
Wrap a Go context.Context with extra accessors that modules need:
- A scoped zap logger named after the module's ID.
- The ability to look up sibling apps and load child modules.
- A handle on the configured
certmagic.Storage, the configured filesystem, and other shared resources. - Cleanup-hook registration tied to the parent config's lifetime.
caddy.Context is the single most-imported type from the core package outside App and Module.
Directory layout
| Path | Role |
|---|---|
context.go |
Context definition, LoadModule, LoadModuleByID, App, Logger, Storage, Filesystems |
caddy.go |
The function that creates contexts during config load (unsyncedDecodeAndRun) |
Key abstractions
| Method | Description |
|---|---|
Context.LoadModule(parent, fieldName) |
Load a module from a json.RawMessage field on parent using its caddy:"namespace=…" tag |
Context.LoadModuleByID(id, raw) |
Load a specific module by ID from a json.RawMessage |
Context.App(name) |
Return the running app with that ID (or nil) |
Context.AppIfConfigured(name) |
Like App, but returns (nil, true) if app is configured but failed to provision |
Context.Logger() |
Return a zap logger named after the calling module's ID |
Context.Storage() |
The configured global certmagic.Storage |
Context.Filesystems() |
Map of named filesystems (caddy.filesystems.*) |
Context.OnCancel(fn) |
Register a function to run when the context is canceled |
Context.Slogger() |
Return an slog.Logger wrapping the same zap logger |
How it works
graph TD
Load[caddy.Load] -->|new Context per config| Ctx[caddy.Context]
Ctx -->|Provision| Module
Module -->|ctx.LoadModule| Child[child module]
Module -->|ctx.App tls| TLSApp[tls app]
Module -->|ctx.Logger| ZapLogger
Reload[next config load] -->|cancel old ctx| Cleanup[CleanerUpper.Cleanup<br/>+ OnCancel hooks]Where contexts come from
caddy.Load (in caddy.go) creates one root Context per config. Each app's Provision(ctx) runs against this root; when the app loads sub-modules (e.g. routes, matchers, handlers), it usually passes the same context or a derived one. The contexts form a tree, but the tree is implicit — there's no explicit hierarchy stored anywhere; everything dies together when the parent is canceled.
Module-scoped logger
ctx.Logger() walks up the call stack heuristically (looking at the type of the immediate caller via reflection) to determine which module it's being called from, then returns a zap logger named after that module's ID. The result is that every log line from http.handlers.file_server carries logger: http.handlers.file_server, and operators can filter on it.
App lookup
ctx.App("tls") returns the running TLS app. The HTTP app uses this to register hostnames; the events app provides the back-channel for cross-app notifications. If the app is not configured, the call returns nil, nil. If the app is configured but failed to provision (rare during normal operation), AppIfConfigured returns (nil, true) so callers can distinguish.
Cleanup
When the next caddy.Load swaps in a new config, the old root context is canceled. Anything registered through OnCancel runs, then every loaded module's Cleanup runs (if it implements CleanerUpper). Failure during cleanup logs but doesn't roll back — by the time we're cleaning up, the new config is already running.
Filesystems
Context.Filesystems() returns the registered filesystems. A custom filesystem (e.g. a virtual fs reading from an embedded FS) implements caddy.filesystems.* and can be referenced by name from handlers like the file server.
Integration points
- Every
Provision-implementing module receives one and usesctx.Logger,ctx.LoadModule,ctx.App. - The HTTP app's
Provision(modules/caddyhttp/app.go) is the canonical example: it loads server-level modules, asks for the TLS app viactx.App, builds route trees, etc. - Tests using the
caddytestharness get realContexts — there's no mock context; tests run against an actual Caddy instance.
Entry points for modification
- Don't store contexts in struct fields; that's against project convention (
AGENTS.md). If you need acontext.Contextlater, derive one from a request or aServer. - New per-context resource? Add a method on
Contextand a backing field inConfig. Be conservative — every addition becomes part of the implicit module API.
Related pages
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.