I’ve been running a bunch of services in Docker for a while, and Caddy is my reverse proxy of choice. It’s simple, fast, and the automatic TLS is fantastic. But there’s one thing that annoyed me for a long time:
Every time I changed the Caddyfile, I had to restart the entire Caddy container.
It’s only a few seconds of downtime, but when Caddy is sitting in front of many services, those seconds hurt — logs go red, monitors fire alerts, and of course I hit refresh like a caveman wondering why everything’s dead.
Then I finally learned that Caddy can actually reload its configuration live.
No restart. No interruption. Zero downtime.
This post is me writing down how I got it working in Docker — partly to help others, partly so that future-me doesn’t forget.
Why restarting is not ideal
Restarting Caddy seems harmless, but in practice it:
- briefly stops forwarding traffic
- closes existing connections
- delays TLS initialization
- makes other containers look offline
- annoys anyone using your services at that moment (including yourself)
The good news: you don’t have to restart anything.
Caddy can hot-reload
Caddy has two built-in ways to reload its configuration:
- via the CLI (
caddy reload) - via the Admin API (
:2019)
Both validate the config and apply it without restarting the process.
My setup
My Caddyfile lives next to my docker-compose.yml, and I mount it into the container like this:
1volumes:
2 - ./Caddyfile:/etc/caddy/Caddyfile
3 - ./config:/config
4 - ./data:/dataSo Caddy always reads /etc/caddy/Caddyfile.
The easiest way: docker exec
This is the method I use most:
1docker exec <container_name> caddy reload \
2 --config /etc/caddy/Caddyfile \
3 --adapter caddyfileReplace <container_name> with your actual Caddy container name.
Example:
1docker exec caddy caddy reloadAnd that’s it — Caddy reloads instantly with zero downtime.
Reloading via the Admin API (optional)
If you prefer using curl, you can expose the admin API safely by binding it only to localhost:
1ports:
2 - "127.0.0.1:2019:2019"Then enable it in your Caddyfile:
{
admin :2019
}
Now you can run:
1curl -X POST \
2 -H "Content-Type: text/caddyfile" \
3 --data-binary @Caddyfile \
4 http://localhost:2019/loadThis is useful if you want automation or external tools to reload Caddy.
But personally, I still prefer:
docker exec caddy caddy reload
My tiny reload script
Because why not:
1#!/bin/bash
2docker exec caddy caddy reloadNow I just type:
1./reloadand Caddy refreshes instantly.
Final thoughts
Caddy has been great for my Docker setup, and hot-reloading the config makes it even smoother. No restarts, no interruptions, and no more “why is everything down again?” moments.
If you’re running Caddy in Docker, switch to the caddy reload workflow.
Your future self (and your uptime graphs) will thank you.