Caddy in front of app’s Nginx via Docker Compose

I’ve upgraded one of my Discourse forum servers, so to open UDP 443 rather than only TCP in the IONOS Cloud Panel firewall (i’m from GB).

I don’t use CDN because this particular forum isn’t big user number, but has lots of categories.

I had some difficulty in ensuring the IP geos through nginx correctly, and the forum performs well now with no issues in /logs.

I have rotating proxy logs set up with Docker Compose and appreciate that Caddy does some automatic redaction of sensitive field in the proxy logs, something i haven’t quite noticed with Nginx before. Though i only recall using proxy logs twice in the 2 years i’ve used Discourse in production.

I’m hoping to gather how much interest there would be if i published a guide going through what commands i ran, and what happened when i ran them, to get from the Beginner docker install guide to the slightly more mobile-friendly forum now - i can include commands for future maintenance, i.e. upgrading Caddy to a newer release, but i haven’t tested these commands on my IONOS L VPS yet.

FWIW, there’s this PR: Add Caddy web server template as nginx alternative - Pull Request #952 - discourse/discourse_docker - GitHub

Thanks, I hadn’t seen that PR. That’s interesting - my setup is slightly different in that I’ve kept Discourse’s nginx web server in place and run Caddy separately in Docker Compose in front of it, mainly to provide HTTP/3 at the edge.

There is one small Discourse-side change: I added a persistent app.yml hook so nginx uses Caddy’s X-Forwarded-For value for the real client IP when requests arrive over the Unix socket. I originally needed a Caddy X-Real-IP workaround, but after adding the nginx-side fix I was able to remove that and verify that Discourse still records the correct client IP.

I’ve also explicitly disabled QUIC 0-RTT/early data in the Caddy configuration while leaving HTTP/3 enabled, to avoid that additional early-data edge case.

My approach also enables Caddy’s HTTP access logging at the site level, which gives me Caddy’s default redaction of sensitive credential headers. I rotate the resulting Docker json-file logs at 25 MB × 3. I noticed the PR currently has its rotating log block in Caddy’s global options, which Caddy’s documentation describes as configuring runtime logging rather than HTTP access logging.

So it isn’t quite a completely untouched standard install, but it’s also a different approach from replacing nginx with Caddy altogether.

I’ll have a closer look at that PR. For now I’m mainly interested in whether there is enough interest in this approach to make a step-by-step guide worthwhile, rather than starting on one immediately.

Just a correction/clarification to what I said above.

When I originally described the result as a:

more mobile-friendly forum, part of what I had noticed was that the initial round trip when opening the iOS Safari PWA had previously felt too slow.


However, I now regret including this statement in my later reply:

Disabling 0-RTT was something I tried while diagnosing the client-IP problem, but it does not appear to have been necessary. Both of the PostgreSQL errors involving unix: continued while 0rtt off was already configured.

The change that appears to have resolved those errors was instead the persistent app.yml hook which modifies nginx so that it uses Caddy’s X-Forwarded-For value for the real client IP when requests arrive over the Unix socket.

I have therefore now removed the 0rtt off setting and restored Caddy’s default behaviour. HTTP/3 is still working and the correct client IP is continuing to pass through to Discourse.

So if there is eventually enough interest for me to put together the step-by-step guide, I would not include disabling QUIC 0-RTT as a required part of the configuration.

You can also do it from Vanilla Nginx: https://quic.nginx.org/