Hot-Reloading Caddy in Docker Without Restarting the Container

How I stopped restarting my Caddy container and started hot-reloading config instantly with zero downtime.

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:

The good news: you don’t have to restart anything.


Caddy can hot-reload

Caddy has two built-in ways to reload its configuration:

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:/data

So 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 caddyfile

Replace <container_name> with your actual Caddy container name.

Example:

1docker exec caddy caddy reload

And 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/load

This 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 reload

Now I just type:

1./reload

and 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.

#caddy   #docker   #reverse-proxy   #devops   #self-hosting