How Caddy's Graceful Config Reloading Works: Zero-Downtime Configuration Updates
Caddy achieves zero-downtime configuration reloads by atomically swapping server instances while reusing network listeners, allowing active connections to complete on the old configuration while new connections immediately use the updated settings.
Caddy's graceful config reloading capability allows the web server to apply configuration changes without interrupting active client connections. This mechanism, implemented in the caddyserver/caddy repository, combines listener reuse, atomic configuration swapping, and multiple reload triggers to ensure continuous availability during updates.
The Three Mechanisms Behind Caddy Graceful Reloads
Admin API and CLI Reloads
The primary method for triggering a graceful reload uses the Admin API endpoint or the caddy reload command. When you execute caddy reload or send a POST request to /load, the request is handled in admin.go where the changeConfig function is invoked (see the call at line 69).
In caddy.go, the changeConfig function (starting at line 145) unmarshals the new configuration, compares it with the currently loaded JSON, and—if different or forced—calls unsyncedDecodeAndRun. This starts the new configuration while the old one continues serving requests, ensuring no active connections are dropped.
Signal-Based Reloads (SIGUSR1)
On POSIX systems, Caddy supports signal-triggered reloads via SIGUSR1. The handler is installed in sigtrap_posix.go starting at line 51.
When the signal arrives, Caddy retrieves the "last-known configuration" using getLastConfig (defined in caddy.go at line 71). This function invokes the callback previously stored by SetLastConfig, effectively reloading the same source file used at startup. If the new configuration differs from the current one, ClearLastConfigIfDifferent (line 62) clears the stored callback to prevent stale references.
Tracking the Source Configuration
To enable SIGUSR1 reloads, the CLI run command records the initial configuration source via SetLastConfig in cmd/commandfuncs.go (see the call at line 257). This bookkeeping allows the signal handler to locate and reload the original configuration file even when Caddy is running as a background process.
How Caddy Preserves Connections During Reloads
Listener Reuse and Socket Handoff
The core mechanism enabling zero-downtime reloads is listener reuse. When Caddy creates a network listener, it stores the file descriptor in a global listenerPool. The function listenReusable in listen.go (line 37) first checks this pool for an existing listener with the same network/address key.
If found, Caddy shares the same file descriptor between the old and new server instances. This allows the old HTTP servers to continue processing in-flight requests while the new configuration begins accepting connections on the same socket. Once all old connections complete, the old server stops, but the socket remains open via the shared descriptor.
Unix Domain Socket Handling
For Unix domain sockets, Caddy implements special lifecycle management in listen_unix.go. Before creating a new listener, Caddy unlinks the socket file to prevent "address already in use" errors. As noted in comments at lines 30‑33 of listeners.go, this unlinking also occurs during graceful exit to ensure no stale socket files block subsequent reloads.
Service Manager Notifications
To integrate with process managers like systemd and launchd, Caddy emits status signals during reloads. Immediately before reloading, Caddy calls notify.Reloading(); after a successful reload, it calls notify.Ready(). These functions are referenced in caddy.go (see line 16), providing a no‑op on unsupported platforms but signaling state changes to service managers on Linux and Windows.
Practical Examples: Reloading Caddy Configurations
You can trigger graceful reloads through three primary interfaces:
Using the CLI:
# Reload via the built-in command (graceful by default)
caddy reload --config ./Caddyfile
# Force a reload even if the config appears unchanged
caddy reload --config ./Caddyfile --force
Using the Admin API:
# Reload via the Admin API (useful from scripts or CI)
curl -X POST \
-H "Content-Type: application/json" \
-H "Cache-Control: must-revalidate" \
--data-binary @caddy.json \
http://localhost:2019/load
Using POSIX Signals:
# Trigger a graceful reload with SIGUSR1 (POSIX only)
kill -USR1 $(pgrep -f '^caddy run')
Summary
- Atomic configuration swapping via
changeConfigincaddy.goallows new server instances to start before old ones stop. - Listener reuse through
listenReusableinlisten.goshares file descriptors between old and new configurations, preventing connection drops. - Multiple reload triggers include the Admin API (
POST /load), thecaddy reloadCLI command, andSIGUSR1signals on POSIX systems. - Service manager integration via
notify.Reloading()andnotify.Ready()ensures systemd and launchd correctly track Caddy's state during transitions.
Frequently Asked Questions
What happens to active connections during a Caddy reload?
Active connections remain attached to the old server instance until they complete. Caddy's listenReusable mechanism in listen.go shares the underlying file descriptor between the old and new configurations, allowing the old server to finish processing in-flight requests while the new server immediately begins accepting fresh connections on the same socket.
How does Caddy handle Unix socket files during graceful reloads?
Caddy unlinks Unix domain socket files before creating new listeners and during graceful shutdown to prevent "address already in use" errors. This logic, implemented in listen_unix.go and referenced in listeners.go (lines 30-33), ensures that stale socket files do not block configuration reloads or prevent Caddy from restarting cleanly.
What is the difference between using the Admin API and SIGUSR1 for reloading?
The Admin API (POST /load) accepts arbitrary JSON configurations directly from HTTP requests or the caddy reload CLI, making it ideal for dynamic updates and CI/CD pipelines. In contrast, SIGUSR1 triggers a reload of the "last-known" configuration file recorded at startup via SetLastConfig in cmd/commandfuncs.go, making it suitable for simple file-based workflows where Caddy runs as a background process.
Does Caddy support graceful reloading on Windows?
While Caddy supports configuration reloading on Windows via the Admin API and caddy reload command, the SIGUSR1 signal-based reload mechanism is POSIX-specific and unavailable on Windows. However, the core graceful reload functionality—atomic configuration swapping and listener reuse—works across all platforms, ensuring zero-downtime updates regardless of the trigger method used.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →