Traefik vs Nginx Proxy Manager vs Caddy for Homelab 2026: The Reverse Proxy Decision Guide
Reading time: ~16 minutes Audience: Homelab owners choosing their first reverse proxy, or migrating from one to another Last tested: June 2026 — Traefik 3.2, Nginx Proxy Manager 2.12, Caddy 2.8
Introduction: The Homelab Reverse Proxy Problem
You have fifteen Docker containers running in your homelab — Nextcloud on port 8080, Pi-hole on port 8180, Plex on port 32400, Home Assistant on port 8123. Your browser autocomplete is a graveyard of 192.168.1.50:XXXX entries, your family refuses to type port numbers, and half your services scream “Not Secure” because you never set up HTTPS. There is a single piece of software that fixes all of this at once: a reverse proxy.
A reverse proxy sits at the edge of your network, receives every incoming HTTP request on standard ports 80 and 443, inspects the domain name in the request header, and routes traffic to the correct backend container. You go from memorizing ports to typing photos.homelab.local, admin.homelab.local, and dash.homelab.local. Every service gets automatic HTTPS via Let’s Encrypt. Your browser lock icon turns green. Your family stops asking why the website looks broken.
But here is the problem: in 2026, there are three dominant reverse proxies in the homelab space — Traefik, Nginx Proxy Manager, and Caddy — and they serve fundamentally different audiences. Pick wrong, and you spend weeks fighting configuration files that feel designed by people who hate you. Pick right, and your reverse proxy becomes the most reliable piece of your infrastructure.
This guide compares all three head-to-head with copy-paste Docker Compose configs, a detailed comparison matrix, a decision tree for five common homelab scenarios, Cloudflare Tunnel integration, and Proxmox deployment notes — everything updated for 2026.
Meet the Three Contenders
Traefik — The Kubernetes-Native Powerhouse
Traefik is the Swiss Army knife of reverse proxies. It was purpose-built for container-native environments: it discovers services automatically by watching Docker socket events or Kubernetes API endpoints. You spin up a new container with the label traefik.http.routers.app.rule=Host("app.example.com") and Traefik wires it into the routing table without a config file edit or a reload.
Why homelabbers love it: Automatic service discovery means zero manual proxy-host configuration. A dashboard at traefik.example.com shows every router, service, and middleware in real time. Middleware chains — rate limiting, IP whitelisting, basic auth, redirect regex — stack like LEGO blocks. Traefik 3.x added Gateway API support, HTTP/3 (QUIC), and OpenTelemetry tracing.
Why homelabbers curse it: The learning curve is real. Traefik has three configuration layers (static, dynamic, and Docker labels) and the documentation assumes you already understand reverse proxy theory. A misplaced YAML indent or a forgotten entrypoints definition produces silent failures. The dashboard is read-only by default — you cannot add a route through the GUI. If you need a GUI for proxy-host management, Traefik is not your tool.
Best for: Advanced users who run 10+ containers, want automatic discovery, and are comfortable with YAML-first configuration. If you already use Portainer or Komodo to manage container stacks, Traefik pairs naturally.
Nginx Proxy Manager — The GUI-First Beginner’s Choice
Nginx Proxy Manager (NPM) wraps the battle-tested Nginx engine in a clean, intuitive web GUI. You open http://your-ip:81, log in, click “Add Proxy Host,” fill in domain name, backend IP:port, toggle “Block Common Exploits,” and click “SSL” to request a Let’s Encrypt certificate. That is the entire workflow. No config files. No YAML. No CLI.
Why homelabbers love it: The barrier to entry is near-zero. If you can fill out a web form, you can run NPM. The GUI shows every proxy host with a green/red status indicator. Let’s Encrypt certificate renewal is fully automatic. Access lists let you add HTTP basic auth to specific hosts with a few clicks. NPM 2.12 added HTTP/2 push support, improved WebSocket handling, and a dark mode for the admin panel.
Why homelabbers curse it: The GUI is the only management interface — there is no API, no IaC path, and no config-as-code workflow. If your NPM database gets corrupted, you must rebuild every proxy host by hand (unless you backed up the SQLite file). Custom Nginx directives require dropping into the “Advanced” tab and writing raw Nginx config — eliminating the GUI’s simplicity advantage. No native Docker socket integration or automatic service discovery.
Best for: Beginners who want a working reverse proxy in 15 minutes. Homelabbers with fewer than 15 services who prefer clicking to typing. Anyone migrating from a manual Nginx setup who wants a GUI wrapper around the same engine they already trust.
Already running NPM? See our dedicated beginner guide: Nginx Proxy Manager Docker Compose
Caddy — The Zero-Config HTTPS Contender
Caddy is the developer-friendly reverse proxy that ships with automatic HTTPS by default. You write a three-line Caddyfile:
app.example.com {
reverse_proxy localhost:8080
}
Caddy obtains a Let’s Encrypt certificate on first launch and renews it silently forever. No certificate settings. No cron jobs. No “SSL” tab to click. It just works. Caddy 2.8 added HTTP/3 support, improved reverse proxy health checks, and a new caddy adapt command for validating configs before reload.
Why homelabbers love it: The configuration syntax is the cleanest of the three. A Caddyfile reads like pseudocode. Zero SSL configuration overhead. Built-in support for the ACME DNS-01 challenge (needed for wildcard certificates and services behind Cloudflare). The caddy-docker-proxy module adds Docker label-based routing similar to Traefik but with Caddy’s simpler syntax.
Why homelabbers curse it: The plugin ecosystem, while growing, is smaller than Nginx’s. Some advanced features — rate limiting, failover, request transformation — require compiling Caddy with non-standard modules using xcaddy. The official Docker image is minimal; adding plugins requires a custom build step in your Dockerfile or using a community image. No native GUI. The Caddyfile syntax, while clean, is unlike any other reverse proxy config format.
Best for: Users who want the simplest possible HTTPS setup. Developers who value clean configuration syntax. Homelabbers who run fewer than 10 services and want a “set and forget” reverse proxy that handles certificates without human intervention.
Head-to-Head Comparison Matrix
| Criteria | Traefik 3.x | Nginx Proxy Manager 2.12 | Caddy 2.8 |
|---|---|---|---|
| Learning curve | Steep (YAML + labels) | Flat (GUI) | Moderate (Caddyfile) |
| GUI management | Dashboard (read-only) | Full GUI admin | None (CLI/config) |
| Auto HTTPS (Let’s Encrypt) | ✅ Built-in | ✅ One-click in GUI | ✅ Zero-config default |
| Automatic service discovery | ✅ Docker/K8s labels | ❌ Manual only | ⚠️ With caddy-docker-proxy plugin |
| Docker Compose complexity | High (static + dynamic configs) | Low (single service + DB) | Low (single service + Caddyfile) |
| Raw config flexibility | Very high | Medium (Advanced tab) | High (Caddyfile) |
| Middleware / plugins | 50+ built-in middlewares | Nginx directives | Plugin ecosystem (xcaddy) |
| HTTP/3 (QUIC) support | ✅ Native in v3 | ❌ (HTTP/2 max) | ✅ Native in v2.8 |
| WebSocket support | ✅ Automatic | ✅ Toggle in GUI | ✅ Automatic |
| API / IaC support | ✅ REST API + CRD | ❌ No API | ✅ Admin API + caddy adapt |
| Resource usage (idle) | ~40 MB RAM | ~25 MB RAM | ~20 MB RAM |
| Docker image size | ~80 MB (alpine) | ~60 MB (alpine) | ~40 MB (alpine) |
| DNS-01 challenge | ✅ Built-in | ❌ Manual advanced config | ✅ Built-in |
| Wildcard certs | ✅ Via DNS-01 | ⚠️ Manual Nginx config | ✅ Via DNS-01 |
| Dashboard / metrics | Built-in dashboard + Prometheus | Basic proxy-host list | None (external monitoring) |
| Best for | 10+ containers, automation first | Beginners, <15 services, GUI-first | Simplicity first, <10 services |
Decision Tree: Which Reverse Proxy Should You Choose?
Answer these five questions in order. The first “yes” you hit is your recommendation.
Scenario 1: “I just want HTTPS on my services. I don’t care about Docker labels, APIs, or advanced routing. Give me a GUI.”
→ Pick Nginx Proxy Manager. You will be up and running in 15 minutes. The GUI eliminates YAML entirely. Everything is point-and-click.
Scenario 2: “I run 15+ containers, add new ones every week, and I want my reverse proxy to detect new services automatically without manual host creation.”
→ Pick Traefik. Docker label-based auto-discovery means you configure routing inside each container’s docker-compose.yml and Traefik picks it up without a reload. No GUI bottleneck.
Scenario 3: “I hate YAML and I hate GUIs. I want the cleanest possible config syntax and I never want to think about SSL certificates again.”
→ Pick Caddy. The Caddyfile syntax is the most readable of any reverse proxy. Automatic HTTPS is truly zero-config — no toggle, no checkbox, no cron job. It just happens.
Scenario 4: “I need advanced routing — sticky sessions, circuit breakers, rate limiting, IP whitelisting, traffic splitting — and I’m comfortable with config files.”
→ Pick Traefik or Caddy, depending on your tolerance for YAML. Traefik has a richer built-in middleware catalog. Caddy requires plugins for some advanced features but the config syntax is easier to maintain long-term. If your routing logic spans multiple conditions, Traefik’s router/middleware/service separation is cleaner.
Scenario 5: “I want to manage my reverse proxy config in Git, review changes in pull requests, and deploy via CI/CD.”
→ Pick Traefik. Its YAML configuration files are Git-ops friendly. You can store traefik.yml and dynamic.yml in version control, review routing changes before deployment, and use Git history to roll back misconfigurations. Caddy’s Caddyfile also works in Git-ops flows, but Traefik’s separation of static and dynamic config gives finer control. NPM has no config-as-code path.
Docker Compose: All Three Side by Side
All three reverse proxies can be deployed in under 5 minutes with these minimal Compose files. Customize the .env variables before starting.
Traefik Docker Compose (v3.2)
# traefik/docker-compose.yml
version: "3.8"
services:
traefik:
image: traefik:v3.2
container_name: traefik
restart: unless-stopped
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "8080:8080" # Dashboard (internal-only recommended)
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro # Docker provider
- ./traefik.yml:/traefik.yml:ro # Static config
- ./dynamic.yml:/dynamic.yml:ro # Dynamic config
- ./certs:/certs # Let's Encrypt storage
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.homelab.local`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.middlewares=auth"
# Basic auth: generate with `htpasswd -nb admin password`
- "traefik.http.middlewares.auth.basicauth.users=admin:$$2y$$10$$..."
networks:
proxy:
external: true
Traefik static config (traefik.yml):
# traefik.yml — Static configuration (read on startup only)
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false # Only expose labeled containers
file:
filename: /dynamic.yml # Services defined in YAML
certificatesResolvers:
letsencrypt:
acme:
email: [email protected]
storage: /certs/acme.json
httpChallenge:
entryPoint: web # HTTP-01 challenge
api:
dashboard: true
insecure: false # Keep internal-only
metrics:
prometheus: {} # /metrics endpoint for Grafana
Nginx Proxy Manager Docker Compose (v2.12)
# nginx-proxy-manager/docker-compose.yml
version: "3.8"
services:
npm:
image: jc21/nginx-proxy-manager:2.12
container_name: npm
restart: unless-stopped
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "81:81" # Admin GUI
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
environment:
- DISABLE_IPV6=true
networks:
- proxy
networks:
proxy:
external: true
After starting, open http://localhost:81 and log in with [email protected] / changeme. Add proxy hosts via the GUI — no config files needed.
New to Docker Compose? Start with our Docker Compose for Beginners guide before deploying.
Caddy Docker Compose (v2.8)
# caddy/docker-compose.yml
version: "3.8"
services:
caddy:
image: caddy:2.8-alpine
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp" # HTTP/3 (QUIC)
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./data:/data # Certificate storage
- ./config:/config # Server config
networks:
- proxy
networks:
proxy:
external: true
Caddyfile example:
# Caddyfile
{
email your-email@example.com
admin off # Disable admin API in production
}
app.homelab.local {
reverse_proxy nextcloud:8080
}
photos.homelab.local {
reverse_proxy immich:3001
}
# Wildcard cert via Cloudflare DNS-01 challenge
# Requires the cloudflare module: xcaddy build --with github.com/caddy-dns/cloudflare
# *.homelab.local {
# tls {
# dns cloudflare {env.CLOUDFLARE_API_TOKEN}
# }
# @app host app.homelab.local
# handle @app {
# reverse_proxy nextcloud:8080
# }
# }
Proxmox Deployment Notes
All three reverse proxies run cleanly inside Proxmox LXC containers or VMs. Here are the deployment specifics for homelab users running Proxmox 8.x:
| Setup | Traefik | NPM | Caddy |
|---|---|---|---|
| Recommended Proxmox host | LXC (privileged, nesting=1) or VM | LXC (any) or VM | LXC (any) or VM |
| Docker inside LXC | Requires features: nesting=1 |
Same | Same |
| Best practice | Dedicated LXC container, 2 vCPU, 2 GB RAM | Dedicated LXC, 1 vCPU, 1 GB RAM | Dedicated LXC, 1 vCPU, 512 MB RAM |
| Port 80/443 access | Forward from router to Proxmox host IP | Forward from router to host IP | Forward from router to host IP |
| Proxmox firewall | Allow ports 80, 443 inbound | Allow ports 80, 443, 81 inbound | Allow ports 80, 443 inbound |
Setting up Proxmox first? Follow our Proxmox Beginner Guide before deploying a reverse proxy container.
All three reverse proxies benefit from being the only service listening on ports 80 and 443 on the Proxmox host. If you run multiple reverse proxies or services that bind to these ports, use macvlan or ipvlan Docker networks to assign dedicated IPs.
Cloudflare Tunnel Integration
Pairing a reverse proxy with Cloudflare Tunnel gives you secure remote access without opening ports 80/443 on your router. This is ideal if you are behind CGNAT or prefer not to expose your home IP.
Architecture
Internet → Cloudflare Edge → cloudflared tunnel → localhost:8080 → Reverse Proxy → Docker backend
The reverse proxy receives requests on an internal port (e.g., 8080) instead of binding directly to 80/443. Cloudflare Tunnel’s cloudflared daemon connects to Cloudflare’s edge and forwards traffic to localhost:8080, which Traefik/NPM/Caddy listens on.
Minimal cloudflared config (config.yml):
tunnel: YOUR-TUNNEL-ID
credentials-file: /etc/cloudflared/credentials.json
ingress:
- hostname: "*.yourdomain.com"
service: http://reverse-proxy:80
- service: http_status:404
Complete Cloudflare Tunnel setup: See our Cloudflare Tunnel Homelab Guide for the full walkthrough, including Tunnel creation, DNS configuration, and security hardening.
Traefik + Cloudflare Tunnel specifics
Traefik needs to trust the X-Forwarded-* headers sent by Cloudflare. Add the following to your traefik.yml:
entryPoints:
websecure:
address: ":443"
forwardedHeaders:
trustedIPs:
- "173.245.48.0/20" # Cloudflare IP ranges
- "103.21.244.0/22"
- "103.22.200.0/22"
# Full list at https://www.cloudflare.com/ips/
Performance Benchmarks (Estimated)
These numbers are approximate, derived from community benchmarks on Reddit r/selfhosted and the Caddy vs Traefik vs Nginx comparison threads. Your mileage will vary based on hardware, backend latency, and TLS configuration.
| Metric | Traefik 3.2 | NPM 2.12 (Nginx) | Caddy 2.8 |
|---|---|---|---|
| Requests/sec (static file, 1KB) | ~18,000 | ~28,000 | ~25,000 |
| Requests/sec (proxy to backend, 1KB) | ~12,000 | ~18,000 | ~16,000 |
| TLS handshake latency (new) | ~12 ms | ~8 ms | ~10 ms |
| Idle RAM | ~40 MB | ~25 MB | ~20 MB |
| Under load RAM (1,000 req/s) | ~120 MB | ~80 MB | ~60 MB |
| Configuration reload time | <50 ms (hot) | <100 ms (reloads Nginx) | <30 ms (graceful) |
Key takeaway: NPM (backed by Nginx) is the raw throughput champion for homelab workloads. Caddy is close behind with less RAM. Traefik trades some throughput for its dynamic discovery features. For a homelab serving family traffic — not a SaaS product with 10,000 concurrent users — any of the three will saturate your gigabit LAN before the reverse proxy becomes the bottleneck.
Internal Links: Building Your Homelab Stack
Each reverse proxy is one piece of a larger homelab infrastructure. Here is how the rest of the SteadyPub content library connects:
| Component | Article | Why It Connects |
|---|---|---|
| Docker Compose basics | Docker Compose for Beginners | Foundation for all three reverse proxy deployments |
| Container management | Portainer Docker Compose Stack | Manage reverse proxy containers with a GUI |
| Local DNS | Pi-hole Docker Compose | Resolve *.homelab.local domains through reverse proxy |
| Alt DNS | AdGuard Home Docker | Alternative DNS for internal domain resolution |
| Cloudflare Tunnel | Cloudflare Tunnel Homelab | Secure remote access without opening ports |
| Monitoring | Grafana Docker Compose | Monitor reverse proxy performance with metrics |
| Monitoring | Prometheus Monitoring Homelab | Collect reverse proxy metrics for Grafana dashboards |
| Hardware | Best Mini PC for Homelab | Hardware for running your reverse proxy 24/7 |
| Hardware | Intel N100 Mini PC Homelab | Low-power, always-on reverse proxy host |
| VPN (alternative) | Self-Hosted VPN Homelab | WireGuard as an alternative to Cloudflare Tunnel |
| SSO (next step) | Vaultwarden Docker Compose | Password manager behind your reverse proxy |
| Identity | Authelia / Authentik SSO | Add single sign-on in front of proxy-protected services |
FAQ: Common Reverse Proxy Questions
Do I need a reverse proxy if I only have three services?
Yes — for HTTPS alone. Without a reverse proxy, you must configure TLS on each service individually. With one, you configure it once. Even for three services, the time savings justify the setup.
Can I run two reverse proxies at the same time?
Technically yes, but avoid it. Two reverse proxies competing for ports 80/443 create routing headaches. Pick one reverse proxy and route all traffic through it. If you need a second (e.g., Traefik for Docker services and NPM for non-Docker services), use a dedicated IP per proxy via macvlan.
Which reverse proxy is best for WebSocket-heavy services (Home Assistant, Uptime Kuma, code-server)?
All three handle WebSockets natively with minimal configuration. Traefik and Caddy detect and proxy WebSocket upgrades automatically. NPM requires toggling the “WebSockets Support” switch in the GUI for each proxy host.
How do I monitor reverse proxy health?
- Traefik: Built-in Prometheus metrics endpoint at
/metrics. Pair with Grafana for dashboards. - NPM: No native metrics. Monitor via container health checks and HTTP status probes.
- Caddy: Built-in Prometheus metrics with the
caddy-http-prometheusplugin.
For general uptime monitoring of your services behind the proxy, add Uptime Kuma to your Docker stack.
Final Recommendation
| You are… | Use… | Because… |
|---|---|---|
| A beginner with 3-10 services | Nginx Proxy Manager | GUI eliminates the learning curve. You’ll be online in 15 minutes. |
| An intermediate tinkerer who values simplicity | Caddy | Cleanest config syntax. Automatic HTTPS with zero effort. |
| An advanced homelabber with 15+ containers and automation | Traefik | Auto-discovery and config-as-code scale with your infrastructure. |
| Someone who wants Git-ops / CI/CD for config | Traefik | YAML configs are version-control friendly. |
| Someone who never wants to edit a text file | Nginx Proxy Manager | GUI-only workflow. |
| Someone who wants the absolute lightest resource footprint | Caddy | 20 MB idle RAM. |
There is no single “best” reverse proxy for everyone. The correct choice depends on how many services you run, how much you value automation versus simplicity, and whether you prefer clicking buttons or editing config files. All three are production-grade, actively maintained, and power thousands of homelabs in 2026.
SteadyPub recommendation: Start with Nginx Proxy Manager if you are new to reverse proxies. Graduate to Traefik once you have 15+ containers and want automatic discovery. Use Caddy if you value clean config syntax and truly zero-touch HTTPS above all else.
Last updated: June 2026. Docker image versions: Traefik 3.2.0, Nginx Proxy Manager 2.12.1, Caddy 2.8.4.