Open-Source Wikis

/

Caddy

/

Caddy

/

Architecture

caddyserver/caddy

Architecture

Caddy is built around three ideas:

  1. A single JSON configuration document drives everything (Config in caddy.go).
  2. Almost every behavior is a module registered through caddy.RegisterModule() (modules.go).
  3. Top-level modules called apps implement Start()/Stop() and run for the lifetime of the config.

This page sketches how those pieces fit together. For deeper dives, see Module system, Apps, and Admin API.

Layers

graph TD
    CLI["cmd/caddy<br/>(spf13/cobra)"] -->|Start, Run, Reload| Core["caddy package<br/>(caddy.go, admin.go)"]
    AdminAPI["Admin HTTP API<br/>localhost:2019"] -->|POST /load| Core
    ConfigAdapters["Config adapters<br/>(Caddyfile, JSON 5, …)"] -->|adapt to JSON| Core
    Core -->|loads apps| HTTP["http app<br/>modules/caddyhttp"]
    Core -->|loads apps| TLS["tls app<br/>modules/caddytls"]
    Core -->|loads apps| PKI["pki app<br/>modules/caddypki"]
    Core -->|loads apps| Events["events app<br/>modules/caddyevents"]
    HTTP -->|hands off TLS| TLS
    HTTP -->|local CA| PKI
    HTTP -->|emit| Events
    TLS -->|emit| Events
    Core -->|persists certs, OCSP, locks| Storage["Storage<br/>(certmagic.Storage)"]
    Core --> Logging["Logging<br/>(zap)"]

The Config struct

caddy.Config (in caddy.go) is the shape of the JSON document Caddy loads:

type Config struct {
    Admin   *AdminConfig
    Logging *Logging
    StorageRaw json.RawMessage `caddy:"namespace=caddy.storage inline_key=module"`
    AppsRaw    ModuleMap       `caddy:"namespace="`
    // …unexported runtime state…
}

Three fields drive almost everything:

  • Admin — the admin HTTP endpoint that exposes /load, /config, /adapt, /pki, /reverse_proxy/upstreams, etc. (see admin.go).
  • StorageRaw — a storage module ID; defaults to caddy.storage.file_system (modules/filestorage/).
  • AppsRaw — a ModuleMap keyed by app module ID (http, tls, pki, events, …).

AppsRaw carries caddy:"namespace=" (empty namespace) which means each key is the module ID for a top-level app. That single struct tag is the seam between the typed config and the dynamic module system.

Module IDs and namespaces

Modules are identified by dot-separated lowercase IDs (modules.go):

  • <name> for an app: http, tls, pki, events.
  • <namespace>.<category>.<name> for everything else: http.handlers.file_server, http.matchers.path, caddy.logging.encoders.json, tls.issuance.acme, caddy.storage.file_system, dns.providers.….

The host module decides which namespace to load from via caddy:"namespace=…" struct tags. The inline_key option tells Caddy where to read the module name when configs are inline objects rather than module maps.

Module lifecycle

Every loaded module follows the same lifecycle hooks (interfaces defined in caddy.go):

graph LR
    A[ModuleInfo.New] --> B[json.Unmarshal]
    B --> C{Provisioner?}
    C -->|yes| D[Provision]
    D --> E{Validator?}
    C -->|no| E
    E -->|yes| F[Validate]
    F --> G[use module]
    E -->|no| G
    G --> H{CleanerUpper?}
    H -->|yes| I[Cleanup on ctx cancel]
    H -->|no| J[done]

Provisioning typically takes a caddy.Context (context.go), through which the module can load child modules, get a scoped logger, look up sibling apps, and register cleanup hooks. The whole tree is provisioned before any app's Start() is called.

Loading and reloading

caddy.Load(cfgJSON, forceReload) is the entry point used by both the CLI (caddy run, caddy reload) and the admin API (POST /load). It:

  1. Computes a hash of the new config; if it matches the running one and forceReload is false, it skips.
  2. Parses the JSON into Config, Provisions the tree, then calls Start() on each app in priority order.
  3. On any failure, runs Cleanup() and rolls back to the previous running config (unsyncedDecodeAndRun in caddy.go).
  4. On success, swaps the running config under a mutex and signals the OS service manager via notify (notify/).

Reloads are graceful: existing connections from the previous server keep running until they finish or the configured shutdown timeout elapses.

HTTP request lifecycle

The HTTP app composes a small middleware chain. For each server in http.servers:

sequenceDiagram
    participant Client
    participant Listener as caddy listener<br/>(listeners.go)
    participant Server as caddyhttp.Server<br/>(server.go)
    participant Routes as RouteList<br/>(routes.go)
    participant Handler as Handler module
    participant Upstream

    Client->>Listener: TCP/QUIC connection
    Listener->>Server: ServeHTTP(w, r)
    Server->>Server: enforce listener wrappers, decode hop-by-hop headers
    Server->>Routes: ApplyMatchersAndCompile -> middleware chain
    Routes->>Handler: ServeHTTP(w, r)
    Handler->>Upstream: optional forward (reverse_proxy, fastcgi)
    Upstream-->>Handler: response
    Handler-->>Server: error or nil
    Server-->>Client: response, log entry

Routes are a list of Route structs (modules/caddyhttp/routes.go) where each route has matchers and handlers. At provision time the route list is compiled into a middleware function. The compiled chain is what serves requests. Subroutes (http.handlers.subroute) recursively embed another route list.

TLS and automatic HTTPS

The tls app owns CertMagic's certificate cache and the issuers. The http app's auto-HTTPS step (modules/caddyhttp/autohttps.go) inspects host matchers in every server, decides which names need certificates, and pushes them into tls.automation.policies. The tls app then provisions managed certificates, drives ACME challenges, and serves them via TLS connection policies. See Automatic HTTPS.

Observability hooks

  • modules/metrics/ registers a /metrics endpoint on the admin API for Prometheus.
  • logging.go and modules/logging/ build a tree of zap loggers driven by JSON config; per-module loggers come from ctx.Logger() (context.go).
  • modules/caddyhttp/tracing/ wraps the handler chain in OpenTelemetry spans.
  • modules/caddyevents/ lets modules emit events that other modules can subscribe to (e.g. for cert renewal hooks).
You want to… Start here
Understand the module registry and namespaces Module system
See how the admin endpoint dispatches requests Admin API
Trace HTTP routing and matchers HTTP app
Learn how certificates are issued TLS app, Automatic HTTPS
Write a Caddyfile that becomes JSON Caddyfile
Build a plugin or add a module Patterns and conventions

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Architecture – Caddy wiki | Factory