To Caddy or Not To Caddy? Or, Musings about Servers, Proxies and Docker

Published in Musings
Tagged as

As a programmer, I can say with all honesty that my knowledge of infrastructure tools is skin-deep at best. Sure, I have written some Dockerfiles in my life, and since I self-host all my projects, I did set up a web server for them… Mostly by following instructions found on the 'Net, often as a part of frameworks' documentation (such as Flask's deployment options page.).

Therefore, my current configuration is a bit of a mishmash of Nginx configs, serving this static site, proxying to a couple Docker containers, terminating HTTPS… It feels like a bit of a mess.

And so, I started thinking: why not use a web server for each of my project (except this site, of course)? It'd be a nice, clean configuration, with my top-level Nginx performing HTTPS termination and proxying to the Docker servers, which would serve static files and proxy to my Rails/Flask/etc applications.

And then I started thinking, what web server should I use? Sure, there's always Nginx, but what else does the ecosystem offer? I started poking around. I needed something that could perform reverse proxying, and also serving static files (assets, error pages, etc). One of the more interesting solutions I found was Caddy.

Caddy is quite interesting. It's written in Go, and from the layman's point of view, it looks similar to Nginx, but with simpler configuration, some more syntactic sugar, and automagic HTTPS (with Let's Encrypt or your provided certificates).

To illustrate Caddy's configuration, here's a Caddyfile I sketched for a Roda application with Webpack-compiled assets.

    auto_https off
    order file_server before reverse_proxy

localhost:80 {
    log {
        output stdout
        format console

    root * /public

    @public_files file {
            try_files {path}

    file_server @public_files
    reverse_proxy backend:4000

This file is incomplete, missing header manipulation (cache control, etc) and error handling, but it is working. It instructs Caddy to look for files in /public and serve them, and failing that, reverse proxy to backend.

Just because this configuration is fairly basic, doesn't mean you can't do more with Caddy. You can do rewriting (including syntax sugar for prefix stripping), multiple configurations based on paths or other rules (similar to Nginx's location directives).

And sometimes, you don't even need configuration. If you cd into a directory and run caddy file-server, it'll serve the files from that directory.

So, Caddy is interesting and neat. And I am tempted to try it in my personal projects. However… I don't feel like it's justified, not in my particular case. Sure, the configuration is cleaner compared to Nginx, but my Nginx configs aren't terribly complicated. Also, unless I uproot Nginx entirely and overhaul my entire structure, I won't be using one of Caddy's major selling points: automagic HTTPS. And I really, really don't want to do that. And, of course, this will add some extra load on my system. Probably not much (running Caddy with Docker with the above configuration reports around 0.1% of CPU and 32 Mb of RAM), but still.

So I guess, right now trying to insert Caddy into my projects would not be wise. Still, Caddy is a nice and interesting web server, something I'll try using in my other projects in place of Nginx.