Table of Contents
Caddy Caddyfile Reload: Docker Bind Mount Inode Bug
When deploying Caddyfile changes to a running Caddy container, the standard rsync command will silently fail to update the container's view of the file. Caddy reload will report “config is unchanged” even though the file on disk is different.
Root Cause
rsync replaces files atomically by writing to a temp file and renaming it over the destination. The rename operation creates a new inode. Docker's single-file bind mount holds a reference to the original inode at mount time. After rsync replaces the file, the container's /etc/caddy/Caddyfile still points to the old inode, so Caddy never sees the new content.
This only affects single-file bind mounts (e.g. a specific file like Caddyfile). Directory bind mounts follow the path and do not have this problem.
Symptom
After running rsync to update the Caddyfile on the host and then running:
docker exec folkzone-caddy caddy reload --config /etc/caddy/Caddyfile
Caddy logs show:
"msg":"config is unchanged"
even though the host file has new content. Verifying with:
docker exec folkzone-caddy grep 'new-domain.folk.zone' /etc/caddy/Caddyfile
returns nothing, confirming the container sees the stale inode.
Fix
Use docker compose restart instead of caddy reload when deploying Caddyfile changes. Restarting the container remounts the bind mount from the current file path, picking up whichever inode is now at that path:
docker compose restart caddy
Do not use –force-recreate unless necessary, as that is slower and causes a longer downtime window. A plain restart is sufficient to refresh the bind mount.
Workaround (avoids restart)
Write to the file in-place on the host using shell redirection rather than rsync. This preserves the inode while updating the content:
cat /path/to/new/Caddyfile > /home/brennan/homelab/caddy/Caddyfile
Or use rsync with the –inplace flag, which writes directly to the existing file without creating a temp file:
rsync --inplace -avz -e "ssh -i ~/.omg-lol-keys/id_ed25519" \ server/caddy/Caddyfile \ brennan@192.168.1.65:/home/brennan/homelab/caddy/Caddyfile
After either of these, caddy reload will work correctly without a restart.
Correct Deployment Procedure
rsync --inplace -avz -e "ssh -i ~/.omg-lol-keys/id_ed25519" \ server/caddy/Caddyfile \ brennan@192.168.1.65:/home/brennan/homelab/caddy/Caddyfile
docker exec folkzone-caddy caddy reload --config /etc/caddy/Caddyfile
Or if you forget –inplace and already ran a plain rsync:
docker compose -f /home/brennan/homelab/docker-compose.yml restart caddy
