1. The problem I’m having:
I assume caddy would renew server certs at least a month before expiry. However, it is less than 19 days from expiry now. The certs are not renewing yet.
2. Error messages and/or full log output:
$ docker logs caddy
{"level":"info","ts":1748226702.0930386,"msg":"maxprocs: Honoring GOMAXPROCS=\"24\" as set in environment"}
{"level":"info","ts":1748226702.0936866,"msg":"GOMEMLIMIT is updated","package":"github.com/KimMachineGun/automemlimit/memlimit","GOMEMLIMIT":243095721984,"previous":9223372036854775807}
{"level":"info","ts":1748226702.093765,"msg":"using config from file","file":"/etc/caddy/Caddyfile"}
{"level":"info","ts":1748226702.1207433,"msg":"adapted config to JSON","adapter":"caddyfile"}
{"level":"info","ts":"2025-05-26T02:31:42.125Z","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":"2025-05-26T02:31:42.125Z","logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc0005a7000"}
{"level":"info","ts":"2025-05-26T02:31:42.126Z","logger":"http.auto_https","msg":"server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https_port":443}
{"level":"info","ts":"2025-05-26T02:31:42.126Z","logger":"http.auto_https","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
{"level":"info","ts":"2025-05-26T02:31:42.127Z","logger":"http","msg":"enabling HTTP/3 listener","addr":":443"}
{"level":"info","ts":"2025-05-26T02:31:42.128Z","logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
{"level":"warn","ts":"2025-05-26T02:31:42.128Z","logger":"http","msg":"HTTP/2 skipped because it requires TLS","network":"tcp","addr":":80"}
{"level":"warn","ts":"2025-05-26T02:31:42.128Z","logger":"http","msg":"HTTP/3 skipped because it requires TLS","network":"tcp","addr":":80"}
{"level":"info","ts":"2025-05-26T02:31:42.128Z","logger":"http.log","msg":"server running","name":"remaining_auto_https_redirects","protocols":["h1","h2","h3"]}
{"level":"info","ts":"2025-05-26T02:31:42.128Z","logger":"http","msg":"enabling automatic TLS certificate management","domains":["*.example.duckdns.org","*.example.mywire.org","*.example.casacam.net"]}
{"level":"warn","ts":"2025-05-26T02:31:42.129Z","logger":"tls","msg":"stapling OCSP","error":"no OCSP stapling for [*.example.casacam.net]: no OCSP server specified in certificate","identifiers":["*.example.casacam.net"]}
{"level":"warn","ts":"2025-05-26T02:31:42.129Z","logger":"tls","msg":"stapling OCSP","error":"no OCSP stapling for [*.example.duckdns.org]: no OCSP server specified in certificate","identifiers":["*.example.duckdns.org"]}
{"level":"warn","ts":"2025-05-26T02:31:42.130Z","logger":"tls","msg":"stapling OCSP","error":"no OCSP stapling for [*.shoebox.mywire.org]: no OCSP server specified in certificate","identifiers":["*.example.mywire.org"]}
{"level":"info","ts":"2025-05-26T02:31:42.130Z","msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
{"level":"info","ts":1748226702.1306832,"msg":"serving initial configuration"}
{"level":"info","ts":"2025-05-26T02:31:42.133Z","logger":"tls","msg":"storage cleaning happened too recently; skipping for now","storage":"FileStorage:/data/caddy","instance":"e1709400-bcd3-4b9b-9992-1ebf301cf030","try_again":"2025-05-27T02:31:42.133Z","try_again_in":86399.999999315}
{"level":"info","ts":"2025-05-26T02:31:42.134Z","logger":"tls","msg":"finished cleaning storage units"}
3. Caddy version:
# docker exec caddy caddy version
v2.10.0 h1:fonubSaQKF1YANl8TXqGcn4IbIRUDdfAkpcsfI/vX5U=
4. How I installed and ran Caddy:
a. System environment:
# lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 12 (bookworm)
Release: 12
Codename: bookworm
# systemd --version
systemd 252 (252.36-1~deb12u1)
+PAM +AUDIT +SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT -GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY +P11KIT +QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD -BPF_FRAMEWORK -XKBCOMMON +UTMP +SYSVINIT default-hierarchy=unified
# docker -v
Docker version 28.1.1, build 4eba377
Furthermore, the Debian above is a running on TrueNAS 24.10.2.2 on top of systemd-nspawn.
b. Command:
No idea. It is started by docker.
c. Service/unit/compose file:
$ cat compose.yaml
---
services:
caddy:
build:
context: .
container_name: caddy
pull_policy: build
restart: unless-stopped
user: "3012:3012"
cap_add:
- NET_ADMIN
env_file:
- ./secrets.env
environment:
- TZ=Europe/London
- GOMAXPROCS=24
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./caddy:/etc/caddy
- ./config:/config:rw
- ./data:/data:rw
- ./srv:/srv
- /mnt/logs/caddy/:/logs
labels:
- "com.centurylinklabs.watchtower.enable=false"
d. My complete Caddy config:
# cat Caddyfile
#################
# General Options
{
email someone@gmail.com
# acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
acme_ca https://acme-v02.api.letsencrypt.org/directory
log {
format json {
time_format iso8601
}
}
}
#############################
# Snippets
# a snippet to log per host
(per_host_log) {
log {
hostnames {args[0]}
output file /logs/{args[0]}/{args[0]}.json {
mode 640
roll_size 100MiB
roll_keep 10
roll_keep_for 720h
}
format json {
time_format iso8601
}
}
}
# a snippet handles v2ray requests
(v2ray) {
# the real deal, v2ray
handle_path /ws {
reverse_proxy http://10.27.0.65:2000 {
transport http {
versions h2c
}
}
}
# the decoy and default, pitch plex media server
handle {
invoke plex
}
}
###############
# routes
&(ha) {
reverse_proxy http://10.27.0.50:8123
}
&(ha2) {
reverse_proxy http://10.27.0.65:8123
}
&(nc) {
reverse_proxy http://10.27.0.65:1780
}
&(plex) {
reverse_proxy http://10.27.0.50:32400
}
#######################
# *.example.duckdns.org
*.example.duckdns.org {
tls {
dns duckdns {$DUCKDNS_API_TOKEN}
}
import per_host_log ha.example.duckdns.org
import per_host_log ha2.example.duckdns.org
import per_host_log nc.example.duckdns.org
import per_host_log plex.example.duckdns.org
import per_host_log video.example.duckdns.org
@ha host ha.example.duckdns.org
handle @ha {
invoke ha
}
@ha2 host ha2.example.duckdns.org
handle @ha2 {
invoke ha2
}
@nc host nc.example.duckdns.org
handle @nc {
invoke nc
}
@plex host plex.example.duckdns.org
handle @plex {
invoke plex
}
@v2ray host video.example.duckdns.org
handle @v2ray {
import v2ray
}
handle {
abort
}
}
#####################
# *.example.casacam.net
*.example.casacam.net {
tls {
dns dynu {$DYNU_API_TOKEN} {
own_domain example.casacam.net
}
}
import per_host_log ha.example.casacam.net
import per_host_log ha2.example.casacam.net
import per_host_log nc.example.casacam.net
import per_host_log plex.example.casacam.net
@ha host ha.example.casacam.net
handle @ha {
invoke ha
}
@ha2 host ha2.example.casacam.net
handle @ha2 {
invoke ha2
}
@nc host nc.example.casacam.net
handle @nc {
invoke nc
}
@plex host plex.example.casacam.net
handle @plex {
invoke plex
}
handle {
abort
}
}
######################
# *.example.mywire.org
*.example.mywire.org {
tls {
dns dynu {$DYNU_API_TOKEN} {
own_domain example.mywire.org
}
}
import per_host_log video.example.mywire.org
@video host video.example.mywire.org
handle @video {
import v2ray
}
handle {
abort
}
}