1. Caddy version (caddy version
):
2.2.1
2. How I run Caddy:
a. System environment:
OS: Ubuntu 20.04.1 LTS
Docker: 19.03.8
Docker Compose: 1.23.2
b. Command:
docker-compose build # sometimes
docker-compose up -d
c. Service/unit/compose file:
Dockerfile
FROM caddy:builder AS builder
RUN xcaddy build \
--with github.com/caddy-dns/cloudflare \
--with github.com/lucaslorentz/caddy-docker-proxy/plugin/v2
FROM caddy:2.2.1
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
docker-compose.yml
version: "3.4"
services:
caddy:
# build: .
# image: caddy_caddy:latest
image: lucaslorentz/caddy-docker-proxy:2.3.2
env_file:
- .env
container_name: caddy
ports:
- 80:80
- 443:443
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- caddy_data:/data
- /var/www:/srv
restart: unless-stopped
volumes:
caddy_data: {}
d. My complete Caddyfile or JSON config:
Currently, whatever the default is for Caddy in Docker
3. The problem I’m having:
I run a server on chrisrees.dev
. I also have a ton of apps running on subdomains in Nginx at the moment, like lidarr.chrisrees.dev
. I recently converted all of my apps to their Docker equivalents, so I figured I could try swapping out Nginx with Caddy since most of my configuration is just simple reverse proxying (aside from Nextcloud).
What I’m looking for is information on:
- How to properly build my own Caddy image
- How to start my built Caddy image with docker-compose
- How to get the base Caddy image to detect port 443 properly (it seems to think it’s just on 80)
- How to get automatic TLS working with Cloudflare and subdomains
As far as I can tell, I’m building my own image, starting it, and it should be exposing port 443 but I listed them above just in case I am doing something wrong.
I understand that I’m in sort of a weird spot where some of this support may be better suited as an issue for caddy-docker-proxy
, but I feel like the core of my current problem is that I am either not adding plugins properly or I am not getting logs from plugins properly when building my own image
4. Error messages and/or full log output:
Current logs (when using caddy_caddy:latest or build: .)
caddy | {"level":"info","ts":1609176454.3482232,"msg":"using provided configuration","config_file":"/etc/caddy/Caddyfile","config_adapter":"caddyfile"}
caddy | {"level":"info","ts":1609176454.3493266,"logger":"admin","msg":"admin endpoint started","address":"tcp/localhost:2019","enforce_origin":false,"origins":["localhost:2019","[::1]:2019","127.0.0.1:2019"]}
caddy | {"level":"info","ts":1609176454.3495293,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc000a71dc0"}
caddy | {"level":"info","ts":1609176454.3495297,"logger":"http","msg":"server is listening only on the HTTP port, so no automatic HTTPS will be applied to this server","server_name":"srv0","http_port":80}
caddy | {"level":"info","ts":1609176454.3500907,"msg":"autosaved config","file":"/config/caddy/autosave.json"}
caddy | {"level":"info","ts":1609176454.3501058,"msg":"serving initial configuration"}
caddy | {"level":"info","ts":1609176454.3510673,"logger":"tls","msg":"cleaned up storage units"}
Logs when running image from caddy-docker-proxy
Attaching to caddy
caddy | 2020/12/28 16:08:44 [INFO] Running caddy proxy server
caddy | {"level":"info","ts":1609171728.469046,"logger":"admin","msg":"admin endpoint started","address":"tcp/localhost:2019","enforce_origin":false,"origins":["localhost:2019","[::1]:2019","127.0.0.1:2019"]}
caddy | {"level":"info","ts":1609171728.4694402,"msg":"autosaved config","file":"/config/caddy/autosave.json"}
caddy | 2020/12/28 16:08:48 [INFO] Running caddy proxy controller
caddy | 2020/12/28 16:08:48 [INFO] CaddyfilePath:
caddy | 2020/12/28 16:08:48 [INFO] LabelPrefix: caddy
caddy | 2020/12/28 16:08:48 [INFO] PollingInterval: 30s
caddy | 2020/12/28 16:08:48 [INFO] ProcessCaddyfile: true
caddy | 2020/12/28 16:08:48 [INFO] ProxyServiceTasks: true
caddy | 2020/12/28 16:08:48 [INFO] IngressNetworks: []
caddy | 2020/12/28 16:08:48 [INFO] Caddy ContainerID: 5488fb365ea90fa13d5b47a6cfabb3733c2afe0456d4b4ab0e5f57ded34a7880
caddy | 2020/12/28 16:08:48 [INFO] IngressNetworksMap: map[01a89dc408f135a76f9882bcada3db9790b906ef01fc109ce3edcd29daf3f0d3:true]
caddy | 2020/12/28 16:08:48 [INFO] Swarm is available: false
caddy | 2020/12/28 16:08:48 [INFO] Skipping default Caddyfile because no path is set
caddy | [INFO] Skipping configs because swarm is not available
caddy | [INFO] Skipping services because swarm is not available
caddy | 2020/12/28 16:08:48 [INFO] New Caddyfile:
caddy | lidarr.chrisrees.dev {
caddy | reverse_proxy 172.18.0.3:8987
caddy | tls internal
caddy | }
caddy | 2020/12/28 16:08:48 [INFO] New Config JSON:
caddy | {"apps":{"http":{"servers":{"srv0":{"listen":[":443"],"routes":[{"match":[{"host":["lidarr.chrisrees.dev"]}],"handle":[{"handler":"subroute","routes":[{"handle":[{"handler":"reverse_proxy","upstreams":[{"dial":"172.18.0.3:8987"}]}]}]}],"terminal":true}]}}},"tls":{"automation":{"policies":[{"subjects":["lidarr.chrisrees.dev"],"issuer":{"module":"internal"}}]}}}}
caddy | 2020/12/28 16:08:48 [INFO] Sending configuration to localhost
caddy | {"level":"info","ts":1609171732.530163,"logger":"admin.api","msg":"received request","method":"POST","host":"localhost:2019","uri":"/load","remote_addr":"127.0.0.1:46382","headers":{"Accept-Encoding":["gzip"],"Content-Length":["403"],"Content-Type":["application/json"],"User-Agent":["Go-http-client/1.1"]}}
caddy | {"level":"info","ts":1609171732.530969,"logger":"admin","msg":"admin endpoint started","address":"tcp/localhost:2019","enforce_origin":false,"origins":["localhost:2019","[::1]:2019","127.0.0.1:2019"]}
caddy | {"level":"info","ts":1609171732.5313625,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc00039a230"}
caddy | {"level":"info","ts":1609171732.5483534,"logger":"http","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 | {"level":"info","ts":1609171732.5483701,"logger":"http","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
caddy | {"level":"warn","ts":1609171732.567109,"logger":"pki.ca.local","msg":"installing root certificate (you might be prompted for password)","path":"storage:pki/authorities/local/root.crt"}
caddy | {"level":"error","ts":1609171732.5672064,"logger":"pki.ca.local","msg":"failed to install root certificate","error":"open /tmp/truststore.413256808.pem: no such file or directory","certificate_file":"storage:pki/authorities/local/root.crt"}
caddy | {"level":"info","ts":1609171732.5674603,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["lidarr.chrisrees.dev"]}
caddy | {"level":"info","ts":1609171732.5676572,"msg":"autosaved config","file":"/config/caddy/autosave.json"}
caddy | {"level":"info","ts":1609171732.5676699,"logger":"admin.api","msg":"load complete"}
caddy | 2020/12/28 16:08:52 [INFO] Successfully configured localhost
caddy | {"level":"info","ts":1609171732.567963,"logger":"tls.obtain","msg":"acquiring lock","identifier":"lidarr.chrisrees.dev"}
caddy | {"level":"info","ts":1609171732.5683274,"logger":"tls.obtain","msg":"lock acquired","identifier":"lidarr.chrisrees.dev"}
caddy | {"level":"info","ts":1609171732.5700362,"logger":"tls.obtain","msg":"certificate obtained successfully","identifier":"lidarr.chrisrees.dev"}
caddy | {"level":"info","ts":1609171732.5700564,"logger":"tls.obtain","msg":"releasing lock","identifier":"lidarr.chrisrees.dev"}
caddy | {"level":"warn","ts":1609171732.5706363,"logger":"tls","msg":"stapling OCSP","error":"no OCSP stapling for [lidarr.chrisrees.dev]: no OCSP server specified in certificate"}
caddy | {"level":"info","ts":1609171732.6066406,"logger":"tls","msg":"cleaned up storage units"}
caddy | {"level":"info","ts":1609171733.0314026,"logger":"admin","msg":"stopped previous server"}
caddy | {"level":"error","ts":1609171741.8624554,"logger":"http.log.error","msg":"dial tcp 172.18.0.3:8987: connect: connection refused","request":{"remote_addr":"162.158.75.158:54778","proto":"HTTP/1.1","method":"GET","host":"lidarr.chrisrees.dev","uri":"/","headers":{"Cf-Connecting-Ip":["68.228.180.79"],"Cf-Visitor":["{\"scheme\":\"https\"}"],"Sec-Fetch-Site":["same-origin"],"Sec-Fetch-Dest":["document"],"Cf-Request-Id":["074bb543eb0000039a38b50000000001"],"Sec-Fetch-User":["?1"],"Accept-Language":["en-US,en;q=0.9"],"Accept-Encoding":["gzip"],"Cf-Ipcountry":["US"],"X-Forwarded-For":["68.228.180.79"],"Upgrade-Insecure-Requests":["1"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"],"Connection":["Keep-Alive"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.101 Safari/537.36"],"Referer":["https://lidarr.chrisrees.dev/"],"Cdn-Loop":["cloudflare"],"Cf-Ray":["608c8b1979c6039a-ORD"],"X-Forwarded-Proto":["https"],"Sec-Gpc":["1"],"Sec-Fetch-Mode":["navigate"],"Cookie":["__cfduid=d3e28b6651b387701da85193607156acc1608691996; LidarrAuth=gb8dp7i7V7QzTXP62U1XSTkeZ9NM%2f8GmZEIuMvAfWhg%3dV7GjWKT4BaoosOBOYiSWRFbOzu0KfdjxkxObfMLKQYDayRVL3N6DgnqC9XAFagLf"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"","proto_mutual":true,"server_name":"lidarr.chrisrees.dev"}},"duration":0.00041201,"status":502,"err_id":"cafxhk8qt","err_trace":"reverseproxy.(*Handler).ServeHTTP (reverseproxy.go:441)"}
caddy | {"level":"error","ts":1609171742.4772556,"logger":"http.log.error","msg":"dial tcp 172.18.0.3:8987: connect: connection refused","request":{"remote_addr":"162.158.75.102:34602","proto":"HTTP/1.1","method":"GET","host":"lidarr.chrisrees.dev","uri":"/favicon.ico","headers":{"Cf-Connecting-Ip":["68.228.180.79"],"Cdn-Loop":["cloudflare"],"Cf-Ipcountry":["US"],"Accept":["image/avif,image/webp,image/apng,image/*,*/*;q=0.8"],"Sec-Fetch-Site":["same-origin"],"Cf-Request-Id":["074bb5464e0000039a9102c000000001"],"Sec-Fetch-Dest":["image"],"Referer":["https://lidarr.chrisrees.dev/"],"Cf-Ray":["608c8b1d4a62039a-ORD"],"Cf-Visitor":["{\"scheme\":\"https\"}"],"Sec-Fetch-Mode":["no-cors"],"Sec-Gpc":["1"],"Accept-Language":["en-US,en;q=0.9"],"Cookie":["__cfduid=d3e28b6651b387701da85193607156acc1608691996; LidarrAuth=gb8dp7i7V7QzTXP62U1XSTkeZ9NM%2f8GmZEIuMvAfWhg%3dV7GjWKT4BaoosOBOYiSWRFbOzu0KfdjxkxObfMLKQYDayRVL3N6DgnqC9XAFagLf; cf_ob_info=502:608c8b1979c6039a:ORD; cf_use_ob=0"],"Connection":["Keep-Alive"],"Accept-Encoding":["gzip"],"X-Forwarded-For":["68.228.180.79"],"X-Forwarded-Proto":["https"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.101 Safari/537.36"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"","proto_mutual":true,"server_name":"lidarr.chrisrees.dev"}},"duration":0.000438083,"status":502,"err_id":"kyg1p6krh","err_trace":"reverseproxy.(*Handler).ServeHTTP (reverseproxy.go:441)"}
caddy | {"level":"error","ts":1609171744.5930917,"logger":"http.log.error","msg":"dial tcp 172.18.0.3:8987: connect: connection refused","request":{"remote_addr":"162.158.75.102:37812","proto":"HTTP/1.1","method":"GET","host":"lidarr.chrisrees.dev","uri":"/favicon.ico","headers":{"Accept-Encoding":["gzip"],"X-Forwarded-Proto":["https"],"Sec-Fetch-Site":["same-origin"],"Cf-Visitor":["{\"scheme\":\"https\"}"],"Sec-Fetch-Dest":["image"],"Accept-Language":["en-US,en;q=0.9"],"Cf-Request-Id":["074bb54e970000039a511a8000000001"],"Cf-Connecting-Ip":["68.228.180.79"],"Connection":["Keep-Alive"],"Cf-Ray":["608c8b2a8a79039a-ORD"],"Pragma":["no-cache"],"Accept":["image/avif,image/webp,image/apng,image/*,*/*;q=0.8"],"Sec-Fetch-Mode":["no-cors"],"Cookie":["__cfduid=d3e28b6651b387701da85193607156acc1608691996; LidarrAuth=gb8dp7i7V7QzTXP62U1XSTkeZ9NM%2f8GmZEIuMvAfWhg%3dV7GjWKT4BaoosOBOYiSWRFbOzu0KfdjxkxObfMLKQYDayRVL3N6DgnqC9XAFagLf; cf_ob_info=502:608c8b1d4a62039a:ORD; cf_use_ob=0"],"Cdn-Loop":["cloudflare"],"Cf-Ipcountry":["US"],"X-Forwarded-For":["68.228.180.79"],"Cache-Control":["no-cache"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.101 Safari/537.36"],"Sec-Gpc":["1"],"Referer":["https://lidarr.chrisrees.dev/"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"","proto_mutual":true,"server_name":"lidarr.chrisrees.dev"}},"duration":0.000503957,"status":502,"err_id":"cbwn20d2g","err_trace":"reverseproxy.(*Handler).ServeHTTP (reverseproxy.go:441)"}
Logs when trying to use Cloudflare as dns
caddy | 2020/12/28 16:52:43 [INFO] New Caddyfile:
caddy | # Empty caddyfile
caddy | 2020/12/28 16:52:43 [INFO] New Config JSON:
caddy | {}
caddy | 2020/12/28 16:52:43 [INFO] Sending configuration to localhost
caddy | 2020/12/28 16:52:46 [INFO] Skipping default Caddyfile because no path is set
caddy | [INFO] Skipping configs because swarm is not available
caddy | [INFO] Skipping services because swarm is not available
caddy | [ERROR] Removing invalid block: parsing caddyfile tokens for 'tls': Caddyfile:4 - Error during parsing: getting DNS provider module named 'cloudflare': module not registered: dns.providers.cloudflare
caddy | lidarr.chrisrees.dev {
caddy | reverse_proxy 172.18.0.3:8987
caddy | tls {
caddy | dns cloudflare
caddy | }
caddy | }
5. What I already tried:
I was using Nginx up until today. If I shut down my Caddy image and start my local Nginx back up, things work fine, so I know ports 80 and 443 are accessible. I also currently point to lets-encrypt certs locally in my Nginx config, so I know things should work when configured properly, I just need to figure out how to do it in Caddy.
Currently, build
, and one of the image
values are commented out in my docker-compose.yml
file because I can’t get it working the way I’d expect. If I run docker-compose up -d
and then docker-compose logs --follow
with image: lucaslorentz/caddy-docker-proxy:2.3.2
, I can see when another app comes online based on tags, etc. If I run with build: .
or image: caddy_caddy:latest
after running docker-compose build
, I can see Caddy come up, but I do not see any of the other logging I was seeing from caddy-docker-compose
, like how it was trying to set up TLS against lidarr.chrisrees.dev
and failing the ACME challenge. Because I can’t see the same logging, I also cannot tell if I’ve built my own image with plugins properly, so I do not know if I can actually use the Cloudflare DNS plugin.
6. Links to relevant resources:
- Docker Hub
- GitHub - lucaslorentz/caddy-docker-proxy: Caddy as a reverse proxy for Docker
- GitHub - caddy-dns/cloudflare: Caddy module: dns.providers.cloudflare
I’ve also dug through many other threads to try and get to a working Caddy server. I’m limited in how many links I can add, so I’m turning this into code snippets:
https://caddy.community/t/reverseproxy-to-local-backend-connection-refused/10580
https://caddy.community/t/cloudflare-dns-challenge-for-tls-some-subdomains-work-others-fail-dns-challenge/9110
https://caddy.community/t/acme-challenge-failing/3829
https://caddy.community/t/guidance-on-caddy-using-cloudflare-as-dns-resolver/8551/3