# How Caddy's Graceful Config Reloading Works: Zero-Downtime Configuration Updates

> Learn how Caddy's graceful config reloading provides zero-downtime updates. Discover how it atomically swaps instances and reuses listeners for seamless changes.

- Repository: [Caddy/caddy](https://github.com/caddyserver/caddy)
- Tags: internals
- Published: 2026-03-03

---

**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`](https://github.com/caddyserver/caddy/blob/main/admin.go)** where the `changeConfig` function is invoked (see the call at line [69](https://github.com/caddyserver/caddy/blob/master/admin.go#L69)).

In **[`caddy.go`](https://github.com/caddyserver/caddy/blob/main/caddy.go)**, the `changeConfig` function (starting at line [145](https://github.com/caddyserver/caddy/blob/master/caddy.go#L145)) 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`](https://github.com/caddyserver/caddy/blob/main/sigtrap_posix.go)** starting at line [51](https://github.com/caddyserver/caddy/blob/master/sigtrap_posix.go#L51).

When the signal arrives, Caddy retrieves the "last-known configuration" using `getLastConfig` (defined in **[`caddy.go`](https://github.com/caddyserver/caddy/blob/main/caddy.go)** at line [71](https://github.com/caddyserver/caddy/blob/master/caddy.go#L71)). 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](https://github.com/caddyserver/caddy/blob/master/caddy.go#L62)) 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`](https://github.com/caddyserver/caddy/blob/main/cmd/commandfuncs.go)** (see the call at line [257](https://github.com/caddyserver/caddy/blob/master/cmd/commandfuncs.go#L257)). 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`](https://github.com/caddyserver/caddy/blob/main/listen.go)** (line [37](https://github.com/caddyserver/caddy/blob/master/listen.go#L37)) 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`](https://github.com/caddyserver/caddy/blob/main/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](https://github.com/caddyserver/caddy/blob/master/listeners.go#L30) of **[`listeners.go`](https://github.com/caddyserver/caddy/blob/main/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`](https://github.com/caddyserver/caddy/blob/main/caddy.go)** (see line [16](https://github.com/caddyserver/caddy/blob/master/caddy.go#L16)), 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:**

```bash

# 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:**

```bash

# 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:**

```bash

# Trigger a graceful reload with SIGUSR1 (POSIX only)

kill -USR1 $(pgrep -f '^caddy run')

```

## Summary

- **Atomic configuration swapping** via `changeConfig` in [`caddy.go`](https://github.com/caddyserver/caddy/blob/main/caddy.go) allows new server instances to start before old ones stop.
- **Listener reuse** through `listenReusable` in [`listen.go`](https://github.com/caddyserver/caddy/blob/main/listen.go) shares file descriptors between old and new configurations, preventing connection drops.
- **Multiple reload triggers** include the Admin API (`POST /load`), the `caddy reload` CLI command, and `SIGUSR1` signals on POSIX systems.
- **Service manager integration** via `notify.Reloading()` and `notify.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`](https://github.com/caddyserver/caddy/blob/main/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`](https://github.com/caddyserver/caddy/blob/main/listen_unix.go) and referenced in [`listeners.go`](https://github.com/caddyserver/caddy/blob/main/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`](https://github.com/caddyserver/caddy/blob/main/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.