caddyserver/caddy
Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Serves HTTP/HTTPS sites with automatic TLS certificates and zero-downtime config reloads
Configuration enters through CLI commands or files, gets parsed and converted to JSON by adapters, validated against module schemas, then applied to the running instance. HTTP requests flow through middleware chains defined by the configuration, while the admin API allows runtime configuration changes without restart. Certificate management happens automatically in background, with certificates stored and renewed as needed.
Under the hood, the system uses 3 feedback loops, 4 data pools, 5 control points to manage its runtime behavior.
A 8-component backend api. 309 files analyzed. Data flows through 6 distinct pipeline stages.
How Data Flows Through the System
Configuration enters through CLI commands or files, gets parsed and converted to JSON by adapters, validated against module schemas, then applied to the running instance. HTTP requests flow through middleware chains defined by the configuration, while the admin API allows runtime configuration changes without restart. Certificate management happens automatically in background, with certificates stored and renewed as needed.
- Parse configuration input — CLI commands read Caddyfile or JSON from disk, Caddyfile text gets tokenized by lexer into Token streams, then parsed into ServerBlock tree structures with import resolution
- Convert to unified JSON — Adapter.Adapt() transforms ServerBlock structures into standardized JSON Config format, resolving imports, substituting variables, and organizing by application type [ServerBlock → Config]
- Load configuration into instance — Instance.Load() validates JSON config against registered module schemas, provisions modules with their configurations, and builds dependency graphs [Config → Module]
- Start services and admin API — Instance.Start() initializes provisioned modules (HTTP servers, TLS managers), starts AdminServer on configured port, begins certificate management tasks [Module] (config: AdminConfig.Listen, AdminConfig.Disabled)
- Handle HTTP requests — Incoming requests flow through HTTP middleware chains defined in configuration, with Context providing module access, logging, and placeholder replacement
- Process configuration changes — Admin API receives new configurations via POST /config endpoints, validates changes, performs graceful reload by starting new modules alongside old ones, then switching atomically [Config]
Data Models
The data structures that flow between stages — the contracts that hold the system together.
caddy.gostruct with Apps map[string]json.RawMessage (module configurations), Admin *AdminConfig (API settings), Logging *LoggingConfig, Storage json.RawMessage (storage backend config)
Created from JSON/Caddyfile input, validated against module schemas, applied to running instance with graceful reload
caddyconfig/caddyfile/parse.gostruct with Keys []string (hostnames/addresses), Segments []Segment (directive blocks), IsSnippet bool
Parsed from Caddyfile text, processed through import resolution and variable substitution, converted to JSON Config
modules.gointerface with CaddyModule() ModuleInfo method, plus optional Provisioner, Validator, CleanerUpper interfaces for lifecycle hooks
Registered at init time, instantiated during config loading, provisioned with dependencies, validated, then used in request handling
caddyconfig/caddyfile/lexer.gostruct with Text string (token content), File string, Line int (source location), Quoted bool, IsSilent bool
Generated by lexer from Caddyfile text, consumed by parser to build ServerBlock tree structure
admin.gostruct with Disabled bool, Listen string (API address), EnforceOrigin bool, Origins []string (CORS), Config *ConfigSettings
Configured from main Config, used to start HTTP admin API server for runtime management
Hidden Assumptions
Things this code relies on but never validates. These are the things that cause silent failures when the system changes.
The global module registry contains all required modules when Load() is called, with no validation that modules referenced in the JSON config actually exist in the registry
If this fails: If a config references an unregistered module (e.g., 'http.handlers.custom_auth'), Load() fails with a cryptic error instead of suggesting available modules or indicating a missing import
caddy.go:Instance.Load
Configuration changes via the admin API are atomic - either fully applied or fully rolled back, but the code doesn't handle partial failures during graceful reload where some modules start successfully but others fail
If this fails: A bad config could leave the server in an inconsistent state with half-old, half-new configuration if reload fails partway through module provisioning
admin.go:AdminServer.handleConfig
Import resolution assumes unlimited recursion depth and doesn't track memory usage during recursive file imports, trusting that filesystem limits prevent infinite loops
If this fails: Carefully crafted Caddyfile imports could exhaust memory or stack space before hitting the cycle detection, causing OOM crashes instead of clean error messages
caddyconfig/caddyfile/parse.go:Parse
Sets certmagic.DefaultACME.Agreed = true globally, assuming all users consent to Let's Encrypt terms without any explicit agreement mechanism or documentation requirement
If this fails: Users unknowingly agree to CA terms of service they may not have read, potentially creating legal liability, especially in corporate environments with compliance requirements
cmd/main.go:init
The admin API bind address from AdminConfig.Listen is always available and not already in use by another process, with no retry or fallback mechanism
If this fails: If another process is using the admin port (default :2019), Caddy fails to start entirely instead of degrading gracefully or using an alternative port
caddy.go:Instance.Start
Admin API requests are processed sequentially and don't interfere with each other, but concurrent config changes aren't synchronized beyond basic HTTP handler isolation
If this fails: Two simultaneous admin API calls modifying different parts of the config could race, with the second overwriting changes from the first without any conflict detection
admin.go:AdminServer
Token arrays from parsing fit entirely in memory as []Token slices, with no streaming or chunked processing for very large Caddyfiles
If this fails: Parsing a multi-megabyte Caddyfile with thousands of server blocks could exhaust available memory, causing OOM kills in memory-constrained environments
caddyconfig/caddyfile/dispenser.go:Dispenser.tokens
The ServerType interface implementation exists and is properly initialized when Adapt() is called, but there's no null check on a.ServerType
If this fails: Calling Adapt() with an uninitialized Adapter causes a nil pointer panic instead of the documented 'no server type' error
caddyconfig/caddyfile/adapter.go:Adapter.Adapt
OS provides at least one command line argument in os.Args, specifically that len(os.Args) >= 1 with args[0] containing the executable name
If this fails: In exotic execution environments or containers where os.Args is empty, Caddy prints a fatal error but the array access could panic before the error message
cmd/main.go:Main
Environment variable substitution in {$VAR} format expects valid environment variable names following shell conventions, but doesn't validate variable name syntax
If this fails: Malformed environment variable references like {$123-invalid} or {$} could cause silent substitution failures or unexpected string replacements
caddyconfig/caddyfile/parse.go:Parse
System Behavior
How the system operates at runtime — where data accumulates, what loops, what waits, and what controls what.
Data Pools
Global map storing all registered module constructors, organized by namespace (http.handlers, tls.providers, etc)
Current running configuration and provisioned module instances, maintained for graceful reloads and cleanup
Persistent storage for TLS certificates, private keys, and ACME account data managed by CertMagic
Dependency tracking for Caddyfile imports to detect cycles and ensure proper resolution order
Feedback Loops
- Graceful Configuration Reload (self-correction, balancing) — Trigger: Admin API receives new configuration. Action: Start new instance alongside current one, validate it works, then atomically switch and cleanup old instance. Exit: New configuration fully applied or rollback on error.
- Certificate Renewal (polling, balancing) — Trigger: Certificate approaching expiration. Action: CertMagic automatically requests renewal from ACME provider, validates challenges, installs new certificate. Exit: Certificate successfully renewed or max retry attempts.
- Import Resolution (recursive, reinforcing) — Trigger: Parser encounters import directive. Action: Load referenced file, parse its content, substitute into current position, track dependencies to prevent cycles. Exit: All imports resolved or cycle detected.
Delays
- Graceful Shutdown (async-processing, ~configurable timeout) — Existing connections complete while new requests are rejected, ensuring zero dropped requests during restarts
- Certificate Challenge (eventual-consistency, ~ACME validation time) — New sites must complete ACME challenge before serving HTTPS, may serve HTTP redirects during provisioning
- Module Provisioning (warmup, ~depends on module complexity) — Configuration changes blocked until all modules successfully provision and validate their settings
Control Points
- Admin API Listen Address (env-var) — Controls: Where admin API server binds for runtime configuration management. Default: CADDY_ADMIN
- Auto HTTPS (runtime-toggle) — Controls: Whether to automatically provision certificates and redirect HTTP to HTTPS. Default: enabled by default
- Certificate Storage Backend (architecture-switch) — Controls: Where certificates are stored (filesystem, consul, etc). Default: filesystem default
- Log Level (env-var) — Controls: Verbosity of system logging from DEBUG to ERROR. Default: INFO default
- Grace Period (hyperparameter) — Controls: How long to wait for connections to close during graceful shutdown. Default: configurable
Technology Stack
Handles automatic TLS certificate provisioning, renewal, and storage with ACME provider integration
Structured logging throughout the system with configurable levels and outputs
CLI framework providing the command structure (start, stop, reload, validate, etc)
HTTP/3 and QUIC protocol implementation for next-generation web serving
Metrics collection and exposure for monitoring server performance and health
Distributed tracing and observability instrumentation for request tracking
HTTP routing for the admin API endpoints with middleware support
Expression evaluation for configuration conditions and dynamic behavior
Key Components
- Instance (orchestrator) — Central coordinator managing server lifecycle, module provisioning, configuration reloads, and graceful shutdowns without dropping connections
caddy.go - AdminServer (gateway) — HTTP API server providing endpoints for configuration management (/config, /load), reverse proxy for pprof debugging, and metrics exposure
admin.go - Adapter (transformer) — Converts Caddyfile syntax to JSON configuration through parsing, import resolution, and server block transformation
caddyconfig/caddyfile/adapter.go - Dispenser (processor) — Token stream processor that provides structured access to parsed tokens with cursor management, nesting awareness, and helper methods for directive parsing
caddyconfig/caddyfile/dispenser.go - ModuleMap (registry) — Global registry mapping module names to their implementations, with namespace organization and registration validation
modules.go - Parser (processor) — Builds hierarchical ServerBlock structures from token streams, handles import directive resolution, and performs variable substitution
caddyconfig/caddyfile/parse.go - Context (adapter) — Request-scoped context that carries Caddy instance reference, module access, storage interfaces, and provides centralized logging and replacement functionality
context.go - Replacer (transformer) — Template engine that substitutes placeholders in configuration strings with runtime values like request headers, environment variables, and computed values
replacer.go
Explore the interactive analysis
See the full architecture map, data flow, and code patterns visualization.
Analyze on CodeSeaRelated Backend Api Repositories
Frequently Asked Questions
What is caddy used for?
Serves HTTP/HTTPS sites with automatic TLS certificates and zero-downtime config reloads caddyserver/caddy is a 8-component backend api written in Go. Data flows through 6 distinct pipeline stages. The codebase contains 309 files.
How is caddy architected?
caddy is organized into 6 architecture layers: Command Interface, Core Instance, Module System, Configuration Layer, and 2 more. Data flows through 6 distinct pipeline stages. This layered structure keeps concerns separated and modules independent.
How does data flow through caddy?
Data moves through 6 stages: Parse configuration input → Convert to unified JSON → Load configuration into instance → Start services and admin API → Handle HTTP requests → .... Configuration enters through CLI commands or files, gets parsed and converted to JSON by adapters, validated against module schemas, then applied to the running instance. HTTP requests flow through middleware chains defined by the configuration, while the admin API allows runtime configuration changes without restart. Certificate management happens automatically in background, with certificates stored and renewed as needed. This pipeline design reflects a complex multi-stage processing system.
What technologies does caddy use?
The core stack includes CertMagic (Handles automatic TLS certificate provisioning, renewal, and storage with ACME provider integration), Zap (Structured logging throughout the system with configurable levels and outputs), Cobra (CLI framework providing the command structure (start, stop, reload, validate, etc)), QUIC-GO (HTTP/3 and QUIC protocol implementation for next-generation web serving), Prometheus Client (Metrics collection and exposure for monitoring server performance and health), OpenTelemetry (Distributed tracing and observability instrumentation for request tracking), and 2 more. A focused set of dependencies that keeps the build manageable.
What system dynamics does caddy have?
caddy exhibits 4 data pools (Module Registry, Instance State), 3 feedback loops, 5 control points, 3 delays. The feedback loops handle self-correction and polling. These runtime behaviors shape how the system responds to load, failures, and configuration changes.
What design patterns does caddy use?
5 design patterns detected: Module Plugin Architecture, Graceful Configuration Reload, Adapter Pattern, Context Propagation, Automatic Resource Management.
Analyzed on April 20, 2026 by CodeSea. Written by Karolina Sarna.