1. The problem I’m having:
Been a while since I’ve used Caddy, and I figured it was worth trying it out with the 2.10.0 update.
My interpretation with the new update is that Caddy will only generate a wildcard certificate when possible as a default, however through testing I cannot get it to replicate this behavior without explicitly using a wildcard.
In the blow example, it generated a certificate for the full FQDN, not the wildcard. I realize that I can just put a wildcard in and it resolves the issue, but is this intended?
I used to use Caddy years ago and my greatest regret was that it generated individual certs and so when I saw the latest changelog I was excited to try out Caddy again.
2. Error messages and/or full log output:
caddy-1 | {"level":"info","ts":1747536071.0738835,"logger":"docker-proxy","msg":"Running caddy proxy server"}
caddy-1 | {"level":"info","ts":1747536071.0773175,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
caddy-1 | {"level":"info","ts":1747536071.077419,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
caddy-1 | {"level":"info","ts":1747536071.077424,"logger":"docker-proxy","msg":"Running caddy proxy controller"}
caddy-1 | {"level":"info","ts":1747536071.0777287,"logger":"docker-proxy","msg":"Start","CaddyfilePath":"/etc/caddy/Caddyfile","EnvFile":"","LabelPrefix":"caddy","PollingInterval":30,"ProxyServiceTasks":true,"ProcessCaddyfile":true,"ScanStoppedContainers":false,"IngressNetworks":"[]","DockerSockets":[""],"DockerCertsPath":[""],"DockerAPIsVersion":[""]}
caddy-1 | {"level":"info","ts":1747536071.0779781,"logger":"docker-proxy","msg":"Caddy ContainerID","ID":"354156ebc4309955f72fb477406a47639619606dd1da6c7540be47f3953d5c86"}
caddy-1 | {"level":"info","ts":1747536071.0780141,"logger":"docker-proxy","msg":"Connecting to docker events","DockerSocket":""}
caddy-1 | {"level":"info","ts":1747536071.0791142,"logger":"docker-proxy","msg":"IngressNetworksMap","ingres":"map[606e44ec52c8562af4cefc79943d0d225da561ef638e35187cb6d664a4290359:true caddy:true]"}
caddy-1 | {"level":"info","ts":1747536071.0826154,"logger":"docker-proxy","msg":"Swarm is available","new":false}
caddy-1 | {"level":"info","ts":1747536071.0864053,"logger":"docker-proxy","msg":"New Caddyfile","caddyfile":"{\n\tacme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}\n\tauto_https prefer_wildcard\n}\ntest.redacted.com {\n\trespond \"Hello World\"\n}\n"}
caddy-1 | {"level":"info","ts":1747536071.086585,"logger":"docker-proxy","msg":"New Config JSON","json":"{\"apps\":{\"http\":{\"servers\":{\"srv0\":{\"listen\":[\":443\"],\"routes\":[{\"match\":[{\"host\":[\"test.redacted.com\"]}],\"handle\":[{\"handler\":\"subroute\",\"routes\":[{\"handle\":[{\"body\":\"Hello World\",\"handler\":\"static_response\"}]}]}],\"terminal\":true}]}}},\"tls\":{\"automation\":{\"policies\":[{\"subjects\":[\"test.redacted.com\"],\"issuers\":[{\"challenges\":{\"dns\":{\"provider\":{\"api_token\":\"{env.CLOUDFLARE_API_TOKEN}\",\"name\":\"cloudflare\"}}},\"module\":\"acme\"}]}]}}}}"}
caddy-1 | {"level":"info","ts":1747536071.0866148,"logger":"docker-proxy","msg":"Sending configuration to","server":"localhost"}
caddy-1 | {"level":"info","ts":1747536071.0869803,"logger":"admin.api","msg":"received request","method":"POST","host":"localhost:2019","uri":"/load","remote_ip":"127.0.0.1","remote_port":"53888","headers":{"Accept-Encoding":["gzip"],"Content-Length":["467"],"Content-Type":["application/json"],"User-Agent":["Go-http-client/1.1"]}}
caddy-1 | {"level":"info","ts":1747536071.0875742,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
caddy-1 | {"level":"info","ts":1747536071.0878258,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc000b96200"}
caddy-1 | {"level":"info","ts":1747536071.0879316,"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}
caddy-1 | {"level":"info","ts":1747536071.0879447,"logger":"http.auto_https","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
caddy-1 | {"level":"info","ts":1747536071.0880811,"logger":"http","msg":"enabling HTTP/3 listener","addr":":443"}
caddy-1 | {"level":"info","ts":1747536071.08816,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
caddy-1 | {"level":"warn","ts":1747536071.088187,"logger":"http","msg":"HTTP/2 skipped because it requires TLS","network":"tcp","addr":":80"}
caddy-1 | {"level":"warn","ts":1747536071.0881906,"logger":"http","msg":"HTTP/3 skipped because it requires TLS","network":"tcp","addr":":80"}
caddy-1 | {"level":"info","ts":1747536071.0881917,"logger":"http.log","msg":"server running","name":"remaining_auto_https_redirects","protocols":["h1","h2","h3"]}
caddy-1 | {"level":"info","ts":1747536071.0881937,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["test.redacted.com"]}
caddy-1 | {"level":"info","ts":1747536071.0885038,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
caddy-1 | {"level":"info","ts":1747536071.0885088,"logger":"admin.api","msg":"load complete"}
caddy-1 | {"level":"info","ts":1747536071.0885646,"logger":"docker-proxy","msg":"Successfully configured","server":"localhost"}
caddy-1 | {"level":"info","ts":1747536071.0890148,"logger":"admin","msg":"stopped previous server","address":"localhost:2019"}
caddy-1 | {"level":"info","ts":1747536071.1013532,"logger":"tls","msg":"storage cleaning happened too recently; skipping for now","storage":"FileStorage:/data/caddy","instance":"6ef8b798-ee09-4631-94ab-1e1faf30d58b","try_again":174762,"try_again_in":8}
caddy-1 | {"level":"info","ts":1747536071.1016986,"logger":"tls","msg":"finished cleaning storage units"}
caddy-1 | {"level":"info","ts":1747536071.1095421,"logger":"tls.obtain","msg":"acquiring lock","identifier":"test.redacted.com"}
caddy-1 | {"level":"info","ts":1747536071.1161454,"logger":"tls.obtain","msg":"lock acquired","identifier":"test.redacted.com"}
caddy-1 | {"level":"info","ts":1747536071.1166034,"logger":"tls.obtain","msg":"obtaining certificate","identifier":"test.redacted.com"}
caddy-1 | {"level":"info","ts":1747536071.1212285,"logger":"tls.issuance.acme","msg":"waiting on internal rate limiter","identifiers":["test.redacted.com"],"ca":"https://acme-v02.api.letsencrypt.org/directory","account":""}
caddy-1 | {"level":"info","ts":1747536071.121238,"logger":"tls.issuance.acme","msg":"done waiting on internal rate limiter","identifiers":["test.redacted.com"],"ca":"https://acme-v02.api.letsencrypt.org/directory","account":""}
caddy-1 | {"level":"info","ts":1747536071.1212451,"logger":"tls.issuance.acme","msg":"using ACME account","account_id":"https://acme-v02.api.letsencrypt.org/acme/acct/redacted","account_contact":[]}
caddy-1 | {"level":"info","ts":1747536072.7148938,"msg":"trying to solve challenge","identifier":"test.redacted.com","challenge_type":"dns-01","ca":"https://acme-v02.api.letsencrypt.org/directory"}
caddy-1 | {"level":"info","ts":1747536081.5241692,"msg":"authorization finalized","identifier":"test.redacted.com","authz_status":"valid"}
caddy-1 | {"level":"info","ts":1747536081.524185,"msg":"validations succeeded; finalizing order","order":"https://acme-v02.api.letsencrypt.org/acme/order/redacted/redacted"}
caddy-1 | {"level":"info","ts":1747536084.922559,"msg":"got renewal info","names":["test.redacted.com"],"window_start":175264,"window_end":175279,"selected_time":175272,"recheck_after":174755,"explanation_url":""}
caddy-1 | {"level":"info","ts":1747536085.432382,"msg":"got renewal info","names":["test.redacted.com"],"window_start":175264,"window_end":175279,"selected_time":175277,"recheck_after":174755,"explanation_url":""}
caddy-1 | {"level":"info","ts":1747536085.432476,"msg":"successfully downloaded available certificate chains","count":2,"first_url":"https://acme-v02.api.letsencrypt.org/acme/cert/redacted"}
caddy-1 | {"level":"info","ts":1747536085.4757652,"logger":"tls.obtain","msg":"certificate obtained successfully","identifier":"test.redacted.com","issuer":"acme-v02.api.letsencrypt.org-directory"}
caddy-1 | {"level":"info","ts":1747536085.4758313,"logger":"tls.obtain","msg":"releasing lock","identifier":"test.redacted.com"}
caddy-1 | {"level":"warn","ts":1747536085.4790637,"logger":"tls","msg":"stapling OCSP","error":"no OCSP stapling for [test.redacted.com]: no OCSP server specified in certificate","identifiers":["test.redacted.com"]}
Id’s and domains have been redacted.
3. Caddy version:
v2.10.0 h1:fonubSaQKF1YANl8TXqGcn4IbIRUDdfAkpcsfI/vX5U=
4. How I installed and ran Caddy:
FROM caddy:builder-alpine AS builder
RUN xcaddy build \
--with github.com/caddy-dns/cloudflare \
--with github.com/lucaslorentz/caddy-docker-proxy/v2
FROM caddy:alpine
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
CMD ["caddy", "docker-proxy"]
a. System environment:
Docker
b. Command:
docker compose up
c. Service/unit/compose file:
services:
caddy:
build:
context: .
restart: unless-stopped
environment:
CLOUDFLARE_API_TOKEN: ${CLOUDFLARE_API_TOKEN}
CADDY_DOCKER_CADDYFILE_PATH: /etc/caddy/Caddyfile
ports:
- 80:80
- 443:443
volumes:
- ./data:data
- /var/run/docker.sock:/var/run/docker.sock
configs:
- source: caddyfile
target: /etc/caddy/Caddyfile
configs:
caddyfile:
content: |
{
acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
test.redacted.com {
respond "Hello World"
}
networks:
default:
name: caddy
external: true
Also tried with auto_https prefer_wildcard
in the global.
d. My complete Caddy config:
{
acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
test.redacted.com {
respond "Hello World"
}