[SOLVED] Building Caddy with caddy-docker-proxy, caddy-dns/cloudflare

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:

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

I decided to just try running the image and passing in my own Caddyfile to see if the Cloudflare DNS plugin is installed and it looks like it is. I’ll just reach out to the proxy plugin guys about how to get logging working

The missing piece in your Dockerfile is that you need to override the default command for the Caddy image with the command used to run docker-proxy.

The CDP plugin adds a caddy docker-proxy command, so you need to add this to the end of your Dockerfile:

CMD ["caddy", "docker-proxy"]
2 Likes

The CDP plugin adds a caddy docker-proxy command, so you need to add this to the end of your Dockerfile

Awesome. I had looked at that as well and was just about to try that. It looks like that got me up and running, thanks!

1 Like

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