Reverse Proxy Comparison: Traefik vs Caddy vs Nginx Proxy Manager
Every home lab that runs more than a couple of services needs a reverse proxy. Without one, you're stuck accessing services by IP address and port number — 192.168.1.50:8096 for Jellyfin, 192.168.1.50:9443 for Portainer, 192.168.1.50:8080 for your dashboard. A reverse proxy lets you use proper hostnames like jellyfin.home.lab and handles HTTPS certificates automatically.
The three most popular options in the home lab community are Traefik, Caddy, and Nginx Proxy Manager (NPM). They all get the job done, but they're built for different kinds of users.

Quick Comparison
| Feature | Traefik | Caddy | Nginx Proxy Manager |
|---|---|---|---|
| Configuration | Labels/YAML/TOML | Caddyfile/JSON | Web GUI |
| Auto HTTPS | Yes (Let's Encrypt, ZeroSSL) | Yes (Let's Encrypt, ZeroSSL) | Yes (Let's Encrypt) |
| Docker integration | Native (labels) | Plugin or manual | Manual (GUI) |
| Dashboard | Built-in | No | Built-in |
| Learning curve | Steep | Low | Very low |
| Performance | Excellent | Excellent | Good |
| Middleware/plugins | Extensive | Moderate | Limited |
| Memory usage | ~50-80MB | ~20-40MB | ~100-150MB |
Nginx Proxy Manager
NPM is the easiest way to get a reverse proxy running. It wraps Nginx in a web GUI where you point and click to create proxy hosts, redirect rules, and SSL certificates. No config files, no command line, no learning curve.
Setup
services:
nginx-proxy-manager:
image: jc21/nginx-proxy-manager:latest
container_name: npm
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "81:81" # Admin panel
volumes:
- ./npm/data:/data
- ./npm/letsencrypt:/etc/letsencrypt
After deployment, open port 81 and log in with the default credentials ([email protected] / changeme). The GUI walks you through adding proxy hosts — pick a domain, point it at a container's IP and port, toggle SSL on, done.
When to Use NPM
- You're new to reverse proxies and want something working in minutes.
- You prefer GUIs over config files.
- You have a relatively static setup that doesn't change often.
- You want the simplest path to HTTPS certificates for your services.
When to Avoid NPM
- You add and remove services frequently — clicking through a GUI for each change gets tedious.
- You want infrastructure-as-code — NPM's config lives in a SQLite database, not in versionable files.
- You need advanced routing rules, rate limiting, or authentication middleware.
Caddy
Caddy's philosophy is radical simplicity. Its configuration file (the Caddyfile) is the most readable of any reverse proxy. Automatic HTTPS is truly automatic — Caddy provisions and renews certificates with zero configuration. You just define your domains and backends, and HTTPS happens.
Setup
services:
caddy:
image: caddy:latest
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile
- ./caddy/data:/data
- ./caddy/config:/config
Caddyfile:
jellyfin.home.lab {
reverse_proxy jellyfin:8096
}
portainer.home.lab {
reverse_proxy portainer:9443 {
transport http {
tls_insecure_skip_verify
}
}
}
grafana.home.lab {
reverse_proxy grafana:3000
}
That's it. Three services, three blocks, automatic HTTPS for all of them. Caddy handles certificate provisioning, renewal, OCSP stapling, and HTTP-to-HTTPS redirects with no additional configuration.
When to Use Caddy
- You want the simplest config file format.
- HTTPS with zero hassle is your priority.
- You're comfortable with text-based configuration but want it to be readable.
- You want a lightweight proxy that doesn't need much RAM.
When to Avoid Caddy
- You want native Docker label-based configuration (Caddy can do this with plugins, but it's not built-in).
- You need a built-in monitoring dashboard.
- You want the broadest middleware ecosystem (Traefik has more options).
Caddy for Internal-Only Services
For services that don't face the internet, Caddy can still provide HTTPS using its internal CA:
{
local_certs
}
proxmox.home.lab {
reverse_proxy 192.168.1.10:8006 {
transport http {
tls_insecure_skip_verify
}
}
}
The local_certs directive tells Caddy to issue certificates from its own CA rather than Let's Encrypt. You'll need to trust Caddy's root CA on your devices, but after that, all your internal services get valid HTTPS.
Traefik
Traefik is the most powerful option and the de facto standard for Docker-heavy environments. Its killer feature is Docker label-based configuration: you add labels to your Docker containers, and Traefik automatically creates routes for them. No central config file to update, no GUI to click through. Spin up a container with the right labels and it's proxied immediately.
Setup
services:
traefik:
image: traefik:latest
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "8080:8080" # Dashboard
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik/traefik.yml:/traefik.yml
- ./traefik/acme.json:/acme.json
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.home.lab`)"
- "traefik.http.routers.dashboard.service=api@internal"
traefik.yml:
api:
dashboard: true
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
websecure:
address: ":443"
providers:
docker:
exposedByDefault: false
certificatesResolvers:
letsencrypt:
acme:
email: [email protected]
storage: /acme.json
httpChallenge:
entryPoint: web
Then on any container you want proxied:
services:
jellyfin:
image: jellyfin/jellyfin:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.jellyfin.rule=Host(`jellyfin.home.lab`)"
- "traefik.http.routers.jellyfin.entrypoints=websecure"
- "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"
- "traefik.http.services.jellyfin.loadbalancer.server.port=8096"
When to Use Traefik
- You run many Docker containers and add/remove services frequently.
- You want each service to define its own routing in its docker-compose file.
- You need advanced middleware — rate limiting, IP whitelisting, authentication, circuit breakers, retry logic.
- You run Kubernetes or Docker Swarm.
When to Avoid Traefik
- You find the label syntax confusing (it is, initially).
- You have a small, static setup where clicking through NPM is easier.
- You don't want to mount the Docker socket.
HTTPS for Home Labs
All three proxies support Let's Encrypt, but home labs have a wrinkle: your services often aren't accessible from the internet, which means the standard HTTP-01 challenge (where Let's Encrypt connects to your server on port 80) doesn't work.
Options:
DNS-01 challenge: All three proxies support DNS challenges. You prove domain ownership by creating a DNS TXT record. Works for internal services. Requires a DNS provider with an API (Cloudflare is the most popular choice).
Cloudflare Tunnel: Put Cloudflare in front and let the tunnel handle HTTPS. Works with any proxy or none at all.
Internal CA: Caddy and Traefik can both act as internal CAs, issuing certificates for
.localor.home.labdomains. You'll need to install the root CA on your devices.Self-signed certificates: Always an option, but you'll get browser warnings. Fine for testing, annoying for daily use.
For most home labs, the DNS-01 challenge with Cloudflare gives the best experience — real certificates, no browser warnings, works for internal services.
Performance
For a typical home lab with 10-30 services, performance differences between these three are irrelevant. They all proxy traffic at line speed and add negligible latency.
Where it matters:
- High connection counts: Traefik and Caddy handle concurrent connections more efficiently than NPM (which runs on Nginx but adds the overhead of its management layer).
- Memory: Caddy is the lightest (
20-40MB). Traefik is moderate (50-80MB). NPM is heaviest (~100-150MB including its web interface). - CPU: All three are negligible for home lab loads. Caddy and Traefik are written in Go and are particularly efficient.
The Verdict
Start with Nginx Proxy Manager if you've never used a reverse proxy. The GUI makes it immediately understandable, and you can always migrate later.
Graduate to Caddy if you want a simpler config file and don't need Docker-native integration. Caddy's Caddyfile is the most pleasant reverse proxy configuration format in existence.
Go with Traefik if you're running a Docker-heavy environment and want services to self-register. The initial learning curve is steeper, but the Docker label approach scales beautifully.
There's no wrong choice here. Pick the one that matches your comfort level and grow from there.