My domain name contains my very rare full name so I redacted it.
1. The problem I’m having:
I’m running Caddy on a remote server with a dedicated IP4 + IP6 address in a docker container.
In my Caddyfile I have a block for the root domain and then a block for wildcard subdomains with the ACME DNS challenge.
The root domain works and returns the content with a valid cert but no subdomain is reachable despite the wildcard cert being granted.
2. Error messages and/or full log output:
curl -vL foo.mydomain.dev
* Could not resolve host: foo.mydomain.dev
* Closing connection 0
curl: (6) Could not resolve host: foo.mydomain.dev
WARN[0000] a network with name vaultwarden exists but was not created for project "caddy".
Set `external: true` to use an existing network
[+] Running 1/1
✔ Container caddy_inwx Created 0.1s
Attaching to caddy_inwx
caddy_inwx | {"level":"info","ts":1753215875.2804193,"msg":"maxprocs: Leaving GOMAXPROCS=2: CPU quota undefined"}
caddy_inwx | {"level":"info","ts":1753215875.2813373,"msg":"GOMEMLIMIT is updated","package":"github.com/KimMachineGun/automemlimit/memlimit","GOMEMLIMIT":906323558,"previous":9223372036854775807}
caddy_inwx | {"level":"info","ts":1753215875.2817318,"msg":"using config from file","file":"/etc/caddy/Caddyfile"}
caddy_inwx | {"level":"info","ts":1753215875.2922359,"msg":"adapted config to JSON","adapter":"caddyfile"}
caddy_inwx | {"level":"warn","ts":1753215875.293181,"msg":"Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies","adapter":"caddyfile","file":"/etc/caddy/Caddyfile","line":2}
caddy_inwx | {"level":"info","ts":1753215875.3132775,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
caddy_inwx | {"level":"info","ts":1753215875.31438,"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_inwx | {"level":"info","ts":1753215875.3144765,"logger":"http.auto_https","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
caddy_inwx | {"level":"debug","ts":1753215875.3145814,"logger":"http.auto_https","msg":"adjusted config","tls":{"automation":{"policies":[{"subjects":["mydomain.dev"]},{"subjects":["*.mydomain.dev"]},{}]}},"http":{"servers":{"remaining_auto_https_redirects":{"listen":[":80"],"routes":[{},{}],"logs":{"logger_names":{"mydomain.dev":["log0"]},"skip_hosts":["*.mydomain.dev"]}},"srv0":{"listen":[":443"],"routes":[{"handle":[{"handler":"subroute","routes":[{"handle":[{"handler":"headers","response":{"deferred":true,"delete":["Server"],"set":{"Strict-Transport-Security":["max-age=31536000;"],"X-Frame-Options":["DENY"],"X-Robots-Tag":["none"],"X-Xss-Protection":["1; mode=block"]}}},{"encodings":{"gzip":{}},"handler":"encode","prefer":["gzip"]},{"body":"🦐","handler":"static_response"}]}]}],"terminal":true},{"handle":[{"handler":"subroute","routes":[{"group":"group4","handle":[{"handler":"subroute","routes":[{"handle":[{"body":"Foo!","handler":"static_response"}]}]}],"match":[{"host":["foo.mydomain.dev"]}]},{"group":"group4","handle":[{"handler":"subroute","routes":[{"handle":[{"body":"Bar!","handler":"static_response"}]}]}],"match":[{"host":["bar.mydomain.dev"]}]},{"group":"group4","handle":[{"handler":"subroute","routes":[{"handle":[{"body":"TEST","handler":"static_response"}]}]}]}]}],"terminal":true}],"tls_connection_policies":[{}],"automatic_https":{},"logs":{"logger_names":{"mydomain.dev":["log0"]},"skip_hosts":["*.mydomain.dev"]}}}}}
caddy_inwx | {"level":"info","ts":1753215875.3187873,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc000686480"}
caddy_inwx | {"level":"debug","ts":1753215875.319458,"logger":"http","msg":"starting server loop","address":"[::]:443","tls":true,"http3":false}
caddy_inwx | {"level":"info","ts":1753215875.3197365,"logger":"http","msg":"enabling HTTP/3 listener","addr":":443"}
caddy_inwx | {"level":"info","ts":1753215875.3203478,"msg":"failed to sufficiently increase receive buffer size (was: 208 kiB, wanted: 7168 kiB, got: 416 kiB). See https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes for details."}
caddy_inwx | {"level":"info","ts":1753215875.3211339,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
caddy_inwx | {"level":"debug","ts":1753215875.3215818,"logger":"http","msg":"starting server loop","address":"[::]:80","tls":false,"http3":false}
caddy_inwx | {"level":"warn","ts":1753215875.3217592,"logger":"http","msg":"HTTP/2 skipped because it requires TLS","network":"tcp","addr":":80"}
caddy_inwx | {"level":"warn","ts":1753215875.3218677,"logger":"http","msg":"HTTP/3 skipped because it requires TLS","network":"tcp","addr":":80"}
caddy_inwx | {"level":"info","ts":1753215875.3219812,"logger":"http.log","msg":"server running","name":"remaining_auto_https_redirects","protocols":["h1","h2","h3"]}
caddy_inwx | {"level":"info","ts":1753215875.3221974,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["*.mydomain.dev","mydomain.dev"]}
caddy_inwx | {"level":"warn","ts":1753215875.324783,"logger":"tls","msg":"stapling OCSP","error":"no OCSP stapling for [*.mydomain.dev]: no OCSP server specified in certificate","identifiers":["*.mydomain.dev"]}
caddy_inwx | {"level":"debug","ts":1753215875.3251655,"logger":"tls.cache","msg":"added certificate to cache","subjects":["*.mydomain.dev"],"expiration":1760974385,"managed":true,"issuer_key":"acme-v02.api.letsencrypt.org-directory","hash":"1592a10e7031cf68f5a77bbb2291b36509c6149c8d0b8ab8f6f0b5ac898f2101","cache_size":1,"cache_capacity":10000}
caddy_inwx | {"level":"debug","ts":1753215875.3253133,"logger":"events","msg":"event","name":"cached_managed_cert","id":"86adf7a3-38ba-47b5-8b22-9a1440c51e13","origin":"tls","data":{"sans":["*.mydomain.dev"]}}
caddy_inwx | {"level":"info","ts":1753215875.3248372,"logger":"tls","msg":"storage cleaning happened too recently; skipping for now","storage":"FileStorage:/data/caddy","instance":"353d6c16-ac3a-4a39-90fd-7124de315bff","try_again":1753302275.3248324,"try_again_in":86399.999999075}
caddy_inwx | {"level":"info","ts":1753215875.3260918,"logger":"tls","msg":"finished cleaning storage units"}
caddy_inwx | {"level":"debug","ts":1753215875.3263664,"logger":"tls","msg":"loading managed certificate","domain":"mydomain.dev","expiration":1760974384,"issuer_key":"acme-v02.api.letsencrypt.org-directory","storage":"FileStorage:/data/caddy"}
caddy_inwx | {"level":"warn","ts":1753215875.3269284,"logger":"tls","msg":"stapling OCSP","error":"no OCSP stapling for [mydomain.dev]: no OCSP server specified in certificate","identifiers":["mydomain.dev"]}
caddy_inwx | {"level":"debug","ts":1753215875.327122,"logger":"tls.cache","msg":"added certificate to cache","subjects":["mydomain.dev"],"expiration":1760974384,"managed":true,"issuer_key":"acme-v02.api.letsencrypt.org-directory","hash":"ac2e8963454df8507992e760787ac8487720339d7a13c2116154ed109cd2f43a","cache_size":2,"cache_capacity":10000}
caddy_inwx | {"level":"debug","ts":1753215875.3272395,"logger":"events","msg":"event","name":"cached_managed_cert","id":"4e68d3c9-3b23-47f5-a785-a186b640aafb","origin":"tls","data":{"sans":["mydomain.dev"]}}
caddy_inwx | {"level":"debug","ts":1753215875.3273563,"logger":"events","msg":"event","name":"started","id":"d9669937-afb8-4d61-bb1b-7910a7c82aca","origin":"","data":null}
caddy_inwx | {"level":"info","ts":1753215875.3277988,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
caddy_inwx | {"level":"info","ts":1753215875.327928,"msg":"serving initial configuration"}
3. Caddy version:
caddy:2.10.0-alpine
4. How I installed and ran Caddy:
create the Docker file:
FROM caddy:2.10.0-builder-alpine AS builder
RUN xcaddy build \
--with github.com/caddy-dns/inwx@v0.4.0
FROM caddy:2.10.0-alpine
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
then create the image:
docker build -t caddy:inwx.
then create the compose.yml:
services:
caddy:
build: .
image: caddy:inwx
container_name: caddy_inwx
restart: unless-stopped
ports:
- 80:80
- 443:443
- 443:443/udp
networks:
- vaultwarden
volumes:
- ./volumes/Caddyfile:/etc/caddy/Caddyfile:ro
- ./volumes/config:/config
- ./volumes/data:/data
environment:
#- DOMAIN=https://mydomain.dev
- EMAIL=admin@mydomain.dev
- LOG_FILE=/data/access.log
networks:
vaultwarden:
name: vaultwarden
then run the container:
sudo docker compose up -d
a. System environment:
OS: CentOS Stream 9
Arch: x86
SystemD: systemd 252 (252-53.el9)
Docker: Docker version 28.3.2, build 578ccf6
b. Command:
sudo docker compose up -d
c. Service/unit/compose file:
Dockerfile:
FROM caddy:2.10.0-builder-alpine AS builder
RUN xcaddy build \
--with github.com/caddy-dns/inwx@v0.4.0
FROM caddy:2.10.0-alpine
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
d. My complete Caddy config:
{
debug
}
mydomain.dev:443 {
log {
level INFO
output file /var/log/caddy {
roll_size 10MB
roll_keep 100
}
}
# Get a cert by using the ACME HTTP-01 challenge.
tls {$EMAIL}
encode gzip
# Headers to improve security.
header {
# Enable HSTS
Strict-Transport-Security "max-age=31536000;"
# Enable cross-site filter (XSS)
X-XSS-Protection "1; mode=block"
# Disallow the site to be rendered within a frame (clickjacking protection)
X-Frame-Options "DENY"
# Prevent search engines from indexing
X-Robots-Tag "none"
# Remove Caddy branding
-Server
}
respond "🦐"
}
*.mydomain.dev {
tls {
dns inwx {
username ********
password ********
}
}
@foo host foo.mydomain.dev
handle @foo {
respond "Foo!"
}
@bar host bar.mydomain.dev
handle @bar {
respond "Bar!"
}
# Fallback for otherwise unhandled domains
handle {
respond "TEST"
}
}