If you run anything in Docker beyond a single container, you eventually need a reverse proxy in front of it. We spent the last month migrating three production stacks across Caddy 2.8, Traefik v3.1, and nginx Proxy Manager 2.11 to see where each one earns its keep — and where it bites you at 2am.
Configuration philosophy: three very different bets
Caddy bets on convention. A working HTTPS site with automatic Let's Encrypt is roughly four lines of Caddyfile:
example.com {
reverse_proxy localhost:3000
}
That's it. ACME challenges, certificate renewal, HTTP→HTTPS redirect, sane HSTS defaults — all built in. The JSON API underneath is verbose, but you rarely touch it unless you're building config programmatically.
Traefik bets on discovery. You don't write routes by hand — you label containers, and Traefik watches Docker (or Kubernetes, or Consul) and rebuilds its routing table on every change:
labels:
- "traefik.http.routers.api.rule=Host(`api.example.com`)"
- "traefik.http.routers.api.tls.certresolver=letsencrypt"
This feels magical the first time you redeploy a container and the proxy reconfigures itself. It feels less magical the third time you debug a label typo that caused a silent 404 with no log line pointing at the cause.
nginx Proxy Manager (NPM) bets on the GUI. It ships as one Docker image with a SQLite-backed admin UI on port 81. You click "Add Proxy Host", paste a domain, pick a target, request a certificate. You never see an nginx.conf directly — it's generated under the hood, and if you outgrow the UI you can extract it.
NPM is not maintained by nginx Inc. — it's a community project (
NginxProxyManager/nginx-proxy-manager). Releases come in spurts; v2.11 shipped February 2026, and the prior tag was roughly eight months earlier. Caddy and Traefik both push minor releases close to monthly.
TLS, performance, and the things that wake you up
All three terminate TLS, but the failure modes differ.
Caddy's ACME implementation is the strongest of the three. We've watched it survive Let's Encrypt rate-limit slowdowns, DNS-01 hand-offs through Cloudflare, and OCSP stapling outages without intervention. The on-demand TLS feature — issuing certificates the first time a hostname is requested — is genuinely unique and useful if you're running multi-tenant sites where you don't know the domain names in advance.
Traefik does ACME well too, but its cert resolver config has more knobs (HTTP-01, TLS-ALPN-01, DNS-01 across roughly 120 providers) and more ways to misconfigure. We've seen Traefik deployed with caServer: acme-staging-v02.api.letsencrypt.org left over from a test, silently issuing staging certs for two weeks before a user complained that their browser was warning them.
NPM relies on the certbot binary inside its container. It works, but storage is the gotcha: the SQLite DB and /data/letsencrypt-acme-challenge directory must live on a persistent volume. We've seen forum threads from operators who put the container on ephemeral storage and re-issued certs on every redeploy until Let's Encrypt rate-limited them for a week.
On raw performance, the gap is smaller than synthetic benchmarks suggest. nginx (and therefore NPM) edges out the others on a wrk test at around 85K req/s for a 1KB response on a 4-core box. Caddy lands around 60K req/s in the same test; Traefik around 50K. For 99% of self-hosted workloads this is irrelevant — your upstream app is the bottleneck long before the proxy is — but if you front a CDN origin or a hot internal API, the difference becomes measurable.
Don't run Caddy or Traefik benchmarks without checking which TLS cipher suite is active. Both default to TLS 1.3 with ChaCha20-Poly1305, which is roughly 30% slower than AES-NI-accelerated AES-GCM on bare metal. Pin
ciphersuitesto AES-128-GCM-SHA256 if you're comparing against nginx, otherwise you're measuring crypto, not proxy throughput.
Picking one for your stack
The decision falls on three axes: who edits the config, how the upstream services come and go, and whether you want a vendor behind it.
Pick Caddy when: You want one human-readable file checked into Git, you're running on a VM or bare metal more than ephemeral containers, and you value automatic HTTPS with zero tuning. The Caddyfile is the cleanest reverse proxy config we've seen — declarative, hierarchical, and short. The Cloudflare DNS module, S3 storage adapter, and on-demand TLS make it practical for multi-tenant SaaS as well. The plugin ecosystem (xcaddy) lets you build a custom binary with modules baked in, which beats Traefik's plugin sandbox for performance.
Pick Traefik when: Your services are containerized and ephemeral. Traefik shines when containers come up and down on their own — Docker Swarm, Nomad, Kubernetes Ingress. The label-driven config means you never SSH to the proxy to add a route; you redeploy the service and the route appears. The built-in dashboard at :8080 is a real operational asset for figuring out which router matched which request. Middlewares (auth, rate-limit, headers, redirects) compose cleanly and are reusable across routers.
Pick nginx Proxy Manager when: You have non-technical co-admins, or you're running a homelab and want to click "renew certificate" rather than write YAML. It's also the easiest to hand off to a colleague who doesn't want to learn a new config format. Just budget for the maintenance gap, put the data volume somewhere durable (a named Docker volume or a bind mount with backups), and don't expose the admin UI on port 81 to the public internet — the default credentials are well-known and brute-forced constantly.
A practical wrinkle: all three have stable Docker images, but only Caddy and Traefik publish signed binaries you can drop onto a Debian or Alpine box without container plumbing. If you're trying to escape Docker entirely, NPM stops being an option. Caddy's apt repository is the lowest-friction install we've benchmarked — apt install caddy gives you a systemd service, log rotation, and a sample Caddyfile in under a minute.
Originally published at pickuma.com. Subscribe to the RSS or follow @pickuma.bsky.social for new reviews.
Top comments (0)