TLDR; It's significantly simpler to configure NGINX Proxy Manager if you have a small amount of config to do in a home lab scale setup.

I've used Apache, Traefik, and now NGINX Proxy Manager for my self hosted development environment. I've also used HAProxy in front of them as a (TCP) load balancer using a single IP address so I can run multiple backends that all deal with their own TLS. Caddy is my preferred choice for spinning up a quick webserver.

I just started using NGINX Proxy Manager, so I haven't discovered the things I don't like yet, but I can tell you why I switched to it.

Apache and Certbot were what I used initially and I should have kept using it. NGINX Proxy Manager isn't a lot different and I've spent so much time switching through solutions that I should have just managed Apache by hand. I'd be way ahead in terms of time spent.

I didn't even make a serious attempt at Caddy because I want to use wildcard TLS certificates with Cloudflare DNS challenges. The DNS plugins aren't built in to the official Docker container and finding instructions for setting up a single wildcard certificate that could be used on multiple sites was a pain.

For example, the page for wildcard certs [1] links to a simple config example [2] and a json config example [3].

The first example is like the start of the owl drawing joke where it starts out "draw a circle". It's easy to understand, but it's too simple. There are two config blocks, foo and bar, that both contain a `respond` directive. One of those could show a simple reverse proxy config and it would help a ton for people trying to learn to use Caddy for TLS termination. The second example is like the "draw the rest of the owl" part of the joke. It dumps you into a full fledged, comprehensive json config example where you'll drown in info if you don't already know how everything works.

Caddy can also be a bit of a confusing black box. For example, I recently ran across the below config. Assume you don't know anything about Caddy and tell me how it's matching requests to route the web socket traffic to the correct backend.

localhost {
reverse_proxy website:8080

@rendezvous {
header Connection *Upgrade*
header Upgrade websocket
}

reverse_proxy @rendezvous rendezvous:4000
}

If I don't know anything about Caddy I don't even know if the `header` lines are matching that header or adding that header. What are the asterisks for? Why use a custom config language when the real HTTP headers are just as concise? Does the difference in capitalization mean something? Isn't something like this easier to skim, reason, and to learn about?

set-header 'Connection: Upgrade'
set-header 'Upgrade: websocket'

For some semi-related annoyance, look at the MDN docs for that [4] header. They use different capitalization in consecutive examples. It's like the goal is to confuse newcomers. How can someone be a developer or a tech doc writer and ignore those kinds of details?

Here's a snippet from my HAProxy config to contrast the Caddy config:

acl is_certbot path_beg /.well-known/acme-challenge/
use_backend http-example-com if is_certbot

Notice the difference? A simple HAProxy config can be written in a way that someone who has no idea how HAProxy works can skim the config and get a pretty good idea of what's going on.

Caddy is probably the best option for a simple, top down config because most of the sites would be super simple and it would be terse / easy to skim once you learn it. I think I would have ended up using it if I didn't have to deal with the hassle of getting the Cloudflare DNS plugin set up on my own.

I've used Traefik the most and I don't like it now. I remember the config for wildcard certificates being really hard to figure out. For http challenges you must tell services which cert resolver to use. For wildcard certificates you must omit the cert resolver config on all configs except one of them. That wasn't documented anywhere when I originally did my setup. That's v2. The v1 config was much more sane, but I don't recall exactly how it worked.

The second issue I have is the same as what you'd get with things like `caddy-docker-proxy`. Even though I have a single container with a wildcard cert doing TLS termination, I have config strewn across a dozen different places. It's really hard to get an overview of what's going on and I have at least 4 lines of config per host where I need to make up parts of the key. Ex:

traefik.http.routers.gitea.tls=true

When you're trying to learn it, understanding where the `gitea` portion of that key comes from is frustrating. The config for external legacy sites (ie: using the `file` provider) are equally confusing. Traefik looks like it makes sense for large deployments where the labels are handled by additional tooling. All of my complaints revolve around the complexity of the config when putting it into `docker-compose.yml` files by hand, so what's most likely happening is that my use case of a home lab scale config doesn't really make sense and I should have picked something that's easier to maintain. Scalability is paid for in complexity, so choosing something that can scale massively for a home lab is often a bad choice based on my experience.

The appeal of NGINX Proxy Manager is that for simple reverse proxying, which is 95% of what I've got, it's really simple. I don't have to learn some newly invented configuration language until I need an advanced config. I don't know NGINX at all and it took me about 15m to get something running behind NGINX Proxy Manager. Compare that to Caddy where it took me a couple hours just to figure out what I'd need to install/configure just to get a wildcard TLS cert or Traefik where I wasted 10s of hours getting to the point where I could use it with the main (negative) reward being that now I have something that's a pain to maintain.

With NGINX Proxy Manager you get an overview of all your TLS termination and certificates in a single place, it only takes a few minutes to get up and going, and there's an escape hatch to provide advanced configs if needed. Plus, there are tons of config examples and troubleshooting resources for NGINX.

1. https://caddyserver.com/docs/automatic-https#wildcard-certif...

2. https://caddyserver.com/docs/caddyfile/patterns#wildcard-cer...

3. https://caddyserver.com/docs/json/

4. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Up...