Caddy reload doesn't reload certificates from disk

1. The problem I’m having:

Hi, I have a work flow where i use an external (not Caddy) tool to manage and renew certificates. New certificates are copy into /etc/caddy/certs and then a rc-service caddy reload is issued.

The issue is that Caddy continues to use the expired certificate and does not reload it from disk. I have to do a full Caddy restart for the certificates to be reloaded.

2. Error messages and/or full log output:

Output from caddy reload

❯ rc-service caddy reload
 * Reloading Caddy web server ...
2024/10/08 04:53:10.810 INFO    using config from file  {"file": "/etc/caddy/Caddyfile"}
2024/10/08 04:53:10.814 INFO    adapted config to JSON  {"adapter": "caddyfile"}
2024/10/08 04:53:10.814 WARN    Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies   {"adapter": "caddyfile", "file": "/etc/caddy/Caddyfile", "line": 45}
 [ ok ]

Caddy log from reloading

{"level":"info","ts":1728363190.8160791,"msg":"config is unchanged"}
{"level":"info","ts":1728363190.816102,"logger":"admin.api","msg":"load complete"}

Caddy log from restarting

{"level":"info","ts":1728363190.8160791,"msg":"config is unchanged"}
{"level":"info","ts":1728363190.816102,"logger":"admin.api","msg":"load complete"}
{"level":"info","ts":1728363595.9644585,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0x40001d9580"}
{"level":"info","ts":1728363596.3831325,"logger":"http.auto_https","msg":"automatic HTTPS is completely disabled for server","server_name":"srv0"}
{"level":"info","ts":1728363596.3831723,"logger":"http.auto_https","msg":"automatic HTTPS is completely disabled for server","server_name":"srv1"}
{"level":"info","ts":1728363596.3842432,"logger":"tls.cache.maintenance","msg":"stopped background certificate maintenance","cache":"0x40001d9580"}
{"level":"info","ts":1728363596.393009,"msg":"shutting down apps, then terminating","signal":"SIGTERM"}
{"level":"warn","ts":1728363596.3930478,"msg":"exiting; byeee!! 👋","signal":"SIGTERM"}
{"level":"info","ts":1728363596.3930943,"logger":"http","msg":"servers shutting down with eternal grace period"}
{"level":"info","ts":1728363596.933898,"logger":"admin","msg":"stopped previous server","address":"localhost:2019"}
{"level":"info","ts":1728363596.9339466,"msg":"shutdown complete","signal":"SIGTERM","exit_code":0}
{"level":"info","ts":1728363597.1825402,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
{"level":"info","ts":1728363597.1829596,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0x40005f6a80"}
{"level":"info","ts":1728363597.1885035,"logger":"http.auto_https","msg":"automatic HTTPS is completely disabled for server","server_name":"srv0"}
{"level":"info","ts":1728363597.1885386,"logger":"http.auto_https","msg":"automatic HTTPS is completely disabled for server","server_name":"srv1"}
{"level":"info","ts":1728363597.189887,"logger":"http.log","msg":"server running","name":"srv1","protocols":["h1","h2","h2c","h3"]}
{"level":"info","ts":1728363597.1899655,"logger":"http","msg":"enabling HTTP/3 listener","addr":":443"}
{"level":"info","ts":1728363597.1901524,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h2c","h3"]}
{"level":"info","ts":1728363597.1905732,"msg":"autosaved config (load with --resume flag)","file":"/var/lib/caddy/.config/caddy/autosave.json"}
{"level":"info","ts":1728363597.1906176,"msg":"serving initial configuration"}
{"level":"info","ts":1728363597.1922827,"logger":"tls","msg":"storage cleaning happened too recently; skipping for now","storage":"FileStorage:/var/lib/caddy/.local/share/caddy","instance":"e94aeb00-7bee-4fc3-b387-93f6196ddb35","try_again":1728449997.1922803,"try_again_in":86399.99999956}
{"level":"info","ts":1728363597.1924562,"logger":"tls","msg":"finished cleaning storage units"}

3. Caddy version:

v2.8.4 => /opt/caddy/src/git/caddy@(devel)

4. How I installed and ran Caddy:

Install using xcaddy

#!/bin/sh
export XCADDY_GO_BUILD_FLAGS="-ldflags '-w -s'"
export GOARCH="arm64"
export CGO_ENABLED=1
export CGO_CFLAGS="-g -flto -ffat-lto-objects -fPIC"
export CGO_LDFLAGS="-pie"
export GOFLAGS="-buildmode=pie"
extras="--with github.com/caddyserver/caddy/v2=/opt/caddy/src/git/caddy \
--with github.com/caddyserver/transform-encoder \
--with github.com/dunglas/caddy-cbrotli \
--with github.com/WeidiDeng/caddy-cloudflare-ip \
--with github.com/darkweak/souin/plugins/caddy@master \
--with github.com/darkweak/souin@master \
--with github.com/darkweak/storages/go-redis/caddy \
--with github.com/fvbommel/caddy-combine-ip-ranges \
"
/root/go/bin/xcaddy build ${extras}

a. System environment:

Alpine Linux aarch64

b. Command:

rc-service caddy reload

c. Service/unit/compose file:

#!/sbin/openrc-run
supervisor=supervise-daemon
name="Caddy web server"
description="Fast, multi-platform web server with automatic HTTPS"
description_checkconfig="Check configuration"
description_reload="Reload configuration without downtime"
: ${caddy_opts:="--config /etc/caddy/Caddyfile --adapter caddyfile"}
command=/usr/sbin/caddy
command_args="run $caddy_opts"
command_user=caddy:caddy
extra_commands="checkconfig"
extra_started_commands="reload"
capabilities="^cap_net_bind_service"
depend() {
    need net localmount netmount
    after firewall
}
checkconfig() {
    ebegin "Checking configuration for $name"
    su ${command_user%:*} -s /bin/sh -c "$command validate $caddy_opts"
    eend $?
}
reload() {
    ebegin "Reloading $name"
    su ${command_user%:*} -s /bin/sh -c "$command reload $caddy_opts"
    eend $?
}
stop_pre() {
    if [ "$RC_CMD" = restart ]; then
        checkconfig || return $?
    fi
}

d. My complete Caddy config:

{
    # debug
    auto_https off
    log {
        output file /var/log/caddy/caddy_main.log {
            roll_disabled
        }
        format json
    }
    servers {
        metrics
        trusted_proxies cloudflare
        protocols h1 h2 h2c h3
    }
    cache {
        redis {
            configuration {
                InitAddress 127.0.0.1:1212
                #InitAddress unix://run/redis/redis-caddy.sock
                SelectDB 3
                Username caddy
                Password RedisPass4499
                ConnWriteTimeout 5s
                #default_cache_control public
            }
        }
    }
}

ip.tnonline.net:443 {
    tls /etc/caddy/certs/ip.tnonline.net_cert.pem /etc/caddy/certs/ip.tnonline.net_privkey.pem
    log {
        output file /var/log/caddy/ip.tnonline.net.log {
            roll_disabled
        }
        format json
    }
    header Content-Type "text/plain; charset=utf-8"
    header Cache-Control "max-age=0, no-store, no-cache, must-revalidate, proxy-revalidate"
    @ok path /
    respond @ok 200 {
        body {client_ip}
        close
    }
    respond 400 {
        body "Forbidden!"
        close
    }

5. Links to relevant resources:

Caddy doesn’t reload if the config hasn’t changed. You can use caddy reload --force to ignore that same-config check.

2 Likes

Thanks. I should have checked the manual more thoroughly.

--force will cause a reload to happen even if the specified config is the same as what Caddy is already running. Can be useful to force Caddy to reprovision its modules, which can have side-effects, for example: reloading manually-loaded TLS certificates.

I guess I should amend the init script to allow for a forced reload. Something like rc-service caddy force-reload. Does that sound OK?

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.