Traefik vs Caddy vs Nginx: Reverse Proxy for Self-Hosters 2026
TL;DR
Three great reverse proxies for self-hosters, each with a different philosophy: Caddy (Apache 2.0, ~58K stars, Go) is the simplest — automatic HTTPS with zero config, readable Caddyfile syntax. Traefik (MIT, ~52K stars, Go) is Docker-native — auto-discovers containers via labels, no config file changes needed when adding new services. Nginx (BSD) is the battle-tested veteran — maximum flexibility and performance, steeper config curve. Most homelabs start with Caddy, teams running many Docker services prefer Traefik.
Key Takeaways
- Caddy: Simplest, automatic HTTPS, great for static sites and straightforward proxying
- Traefik: Docker-native, zero-downtime reloads, automatic service discovery via labels
- Nginx: Most flexible, highest performance, requires manual SSL cert renewal (or nginx-proxy)
- Auto TLS: Caddy and Traefik handle Let's Encrypt automatically; Nginx needs Certbot
- Config hot-reload: All three support it; Traefik does it without a restart
- Performance: All can handle tens of thousands of concurrent connections — not a bottleneck
Feature Comparison
| Feature | Caddy | Traefik v3 | Nginx |
|---|---|---|---|
| License | Apache 2.0 | MIT | BSD-2-Clause |
| GitHub Stars | ~58K | ~52K | ~20K (mainline) |
| Auto HTTPS | Yes (built-in) | Yes (ACME) | No (need Certbot) |
| Docker label routing | No (manual) | Yes (native) | No (need nginx-proxy) |
| Config language | Caddyfile | TOML/YAML/labels | Nginx config (nginx.conf) |
| Config hot-reload | Yes | Yes (zero downtime) | Yes (nginx -s reload) |
| Built-in auth | Basic auth | Basic auth | Basic auth |
| Rate limiting | Plugin | Middleware | Module (nginx-plus or lua) |
| Load balancing | Yes | Yes | Yes |
| WebSocket support | Yes | Yes | Yes |
| gRPC support | Yes | Yes | Yes |
| Metrics | /metrics endpoint | Prometheus built-in | With stub_status |
| RAM usage | ~30MB | ~50MB | ~10MB |
Option 1: Caddy — Simplest Auto-HTTPS
Caddy has the most human-friendly configuration. One line per site, automatic HTTPS, no certificate management.
Caddy Docker Setup
# docker-compose.yml
services:
caddy:
image: caddy:alpine
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp" # HTTP/3
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
networks:
- proxy
volumes:
caddy_data:
caddy_config:
networks:
proxy:
external: true
# Create shared network first:
docker network create proxy
Caddyfile Examples
# Single site — auto HTTPS:
app.yourdomain.com {
reverse_proxy localhost:8080
}
# Multiple sites:
grafana.yourdomain.com {
reverse_proxy grafana:3000
}
paperless.yourdomain.com {
reverse_proxy paperless:8000
}
# Basic auth:
private.yourdomain.com {
basicauth {
admin JDJhJDE0JG... # caddy hash-password
}
reverse_proxy localhost:9000
}
# Strip path prefix:
yourdomain.com/api/* {
uri strip_prefix /api
reverse_proxy api-service:8080
}
# Multiple upstreams (load balance):
api.yourdomain.com {
reverse_proxy {
to localhost:8081
to localhost:8082
lb_policy round_robin
}
}
Wildcard TLS (DNS challenge)
For *.yourdomain.com wildcard certs (requires DNS API):
*.yourdomain.com {
tls {
dns cloudflare {env.CF_API_TOKEN}
}
@grafana host grafana.yourdomain.com
handle @grafana {
reverse_proxy grafana:3000
}
}
Use caddy:cloudflare Docker image (with DNS plugin):
image: ghcr.io/caddybuilds/caddy-cloudflare:latest
Option 2: Traefik — Docker-Native Auto-Discovery
Traefik discovers services automatically by reading Docker container labels. Add a new container → Traefik routes to it without any config file changes.
Traefik Docker Setup
# docker-compose.yml
services:
traefik:
image: traefik:v3
container_name: traefik
restart: unless-stopped
command:
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=proxy"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.email=you@yourdomain.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- traefik_certs:/letsencrypt
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.yourdomain.com`)"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.middlewares=auth"
- "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$hash"
networks:
- proxy
volumes:
traefik_certs:
networks:
proxy:
external: true
Adding Services via Labels
Any container on the proxy network with Traefik labels gets automatically routed:
# Any other service's docker-compose.yml:
services:
grafana:
image: grafana/grafana:latest
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.grafana.rule=Host(`grafana.yourdomain.com`)"
- "traefik.http.routers.grafana.tls.certresolver=letsencrypt"
- "traefik.http.services.grafana.loadbalancer.server.port=3000"
networks:
proxy:
external: true
No Traefik config changes needed. Start the container → routing is live.
Traefik Middlewares
Apply reusable middleware via labels:
labels:
# Rate limiting:
- "traefik.http.middlewares.ratelimit.ratelimit.average=100"
- "traefik.http.middlewares.ratelimit.ratelimit.burst=50"
# Apply to router:
- "traefik.http.routers.myapp.middlewares=ratelimit@docker"
# Strip prefix:
- "traefik.http.middlewares.stripprefix.stripprefix.prefixes=/app"
# Forward auth (SSO via Authentik):
- "traefik.http.middlewares.authentik.forwardauth.address=https://auth.yourdomain.com/outpost.goauthentik.io/auth/traefik"
Option 3: Nginx — Battle-Tested Flexibility
Nginx is the most widely deployed web server and reverse proxy. Maximum flexibility, best for complex routing rules, requires manual TLS management.
Nginx Docker Setup
services:
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./conf.d:/etc/nginx/conf.d:ro
- /etc/letsencrypt:/etc/letsencrypt:ro
- certbot_webroot:/var/www/certbot:ro
certbot:
image: certbot/certbot:latest
volumes:
- /etc/letsencrypt:/etc/letsencrypt
- certbot_webroot:/var/www/certbot
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
volumes:
certbot_webroot:
Nginx Config Example
# /etc/nginx/conf.d/app.conf
server {
listen 80;
server_name app.yourdomain.com;
# Let's Encrypt renewal:
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name app.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/app.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.yourdomain.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Get certificates with Certbot:
certbot certonly --webroot -w /var/www/certbot \
-d app.yourdomain.com --email you@example.com --agree-tos
Decision Guide
Choose Caddy if:
- You're new to reverse proxies
- You want the fastest path to HTTPS — minimal config
- You run a small number of services (under ~20)
- You prefer editing a config file to managing labels
- Your sites are mostly static or simple reverse proxies
Choose Traefik if:
- You run many Docker services and want zero-touch routing
- Services are added/removed frequently
- You want a dashboard showing all routes
- You're already in a Docker Compose-heavy workflow
- You need advanced middleware (rate limiting, auth, headers) per-service
Choose Nginx if:
- You need maximum flexibility and fine-grained control
- You have complex routing rules (regex, geo-IP, etc.)
- You serve static files at high scale
- You need the nginx ecosystem (ModSecurity WAF, GeoIP2, etc.)
- You're familiar with Nginx and don't want to learn a new tool
See all open source infrastructure tools at OSSAlt.com/categories/infrastructure.