Choosing a reverse proxy is one of the first big decisions when self-hosting. It sits in front of all your services, handles SSL certificates, and routes traffic to the right container. Pick the wrong one and you’ll waste hours fighting configuration files.

This guide compares the three most popular options — Nginx Proxy Manager, Caddy, and Traefik — from a beginner’s perspective. By the end, you’ll know exactly which one to start with.

What Is a Reverse Proxy?

A reverse proxy accepts incoming web requests and forwards them to the correct backend service. Instead of exposing each service on a different port (like server:8080, server:3000), you access everything through clean URLs like photos.yourdomain.com or cloud.yourdomain.com.

Key benefits:

  • Single entry point — only ports 80 and 443 need to be open
  • Automatic SSL — free HTTPS certificates from Let’s Encrypt
  • Clean URLs — subdomain or path-based routing
  • Security — hide internal service ports from the internet

The Three Contenders

FeatureNginx Proxy ManagerCaddyTraefik
Config styleWeb GUICaddyfile (text)Labels + YAML
SSL automation✅ Built-in✅ Built-in (best)✅ Built-in
Learning curveVery lowLowMedium
Docker integrationManual setupManual setupNative (labels)
PerformanceExcellentExcellentExcellent
Advanced featuresLimitedModerateExtensive
Community sizeLargeGrowing fastLarge

Option 1: Nginx Proxy Manager (NPM)

Best for: Complete beginners who want a GUI.

Nginx Proxy Manager wraps the battle-tested Nginx web server in a friendly web interface. You click buttons instead of editing config files.

Setup with Docker Compose

services:
  npm:
    image: jc21/nginx-proxy-manager:latest
    container_name: nginx-proxy-manager
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "81:81"  # Admin panel
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt

Start it up:

docker compose up -d

Visit http://your-server-ip:81 and log in with the default credentials ([email protected] / changeme). You’ll be prompted to change them immediately.

Adding a Service

  1. Click Proxy HostsAdd Proxy Host
  2. Enter your domain: jellyfin.yourdomain.com
  3. Set the forward hostname to the container name or IP
  4. Set the forward port (e.g., 8096)
  5. Click the SSL tab → select Request a new SSL Certificate
  6. Enable Force SSL and HTTP/2 Support
  7. Save

That’s it. Your service is now accessible over HTTPS.

Pros

  • Zero config files — everything through the web UI
  • Visual feedback — see all your proxy hosts at a glance
  • Access lists — basic IP-based access control built in
  • Stream support — can proxy TCP/UDP streams (for game servers, etc.)

Cons

  • Manual setup — you add each service by hand
  • No auto-discovery — won’t detect new Docker containers
  • Limited customization — advanced Nginx configs require workarounds
  • Extra port — the admin panel needs port 81

Option 2: Caddy

Best for: People comfortable with a text editor who want the simplest config files.

Caddy’s killer feature is automatic HTTPS by default. Just put a domain name in the config and Caddy handles certificates, renewals, redirects — everything. No extra configuration needed.

Setup with Docker Compose

services:
  caddy:
    image: caddy:2-alpine
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./caddy_data:/data
      - ./caddy_config:/config

The Caddyfile

Create a Caddyfile in the same directory:

j}n}v}eealxultlyrcrtrfeleweivovavneuere.rdrdrys.sesoeyeneu_o_._rpupypdrrroroodouomxoxrxaymydyiaonjinmv.eneaacl.xiuolctnlmyoc.tfmlcw{iooan{umr:dd8:{e08n90:680

That’s the entire configuration. Three lines per service. Caddy automatically:

  • Obtains Let’s Encrypt certificates
  • Redirects HTTP to HTTPS
  • Renews certificates before they expire
  • Enables HTTP/2 and HTTP/3

Adding Middleware

Need basic auth, rate limiting, or header manipulation? Caddy makes it clean:

a}f}dimlienb}rseh}r.ae.neeysvycavoieoodeucarudeXXrradsrer--sdumedCFeoti_og{or_mhnpmznaparaitmri{$oipeeon2xnn-x.ay.tOyc$c-po1poTtfm4omyii$rpol{ht{eneaa-sbsiOrhnpDogetEworiNse:oYes9nrh0s:e08r0n0eo8s0niff

Pros

  • Simplest config syntax — readable by anyone
  • HTTPS just works — no SSL tab, no checkboxes, no DNS challenge setup
  • HTTP/3 support — modern protocol support out of the box
  • Lightweight — single binary, small memory footprint
  • Great documentation — clear, well-organized docs

Cons

  • No web GUI — you edit a text file (though some third-party UIs exist)
  • No auto-discovery — you manually add services to the Caddyfile
  • Reload required — need to restart or reload after config changes
  • Smaller plugin ecosystem — fewer extensions than Nginx or Traefik

Option 3: Traefik

Best for: Docker power users who want automatic service discovery.

Traefik’s superpower is Docker integration. Add labels to your containers and Traefik automatically creates routes — no config file changes needed. Spin up a new service and it’s instantly accessible.

Setup with Docker Compose

services:
  traefik:
    image: traefik:v3
    container_name: traefik
    restart: unless-stopped
    command:
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
      - "[email protected]"
      - "--certificatesresolvers.letsencrypt.acme.storage=/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./acme.json:/acme.json
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dashboard.rule=Host(`traefik.yourdomain.com`)"
      - "traefik.http.routers.dashboard.service=api@internal"
      - "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"

Adding a Service

Instead of editing Traefik’s config, you add labels to the service itself:

services:
  jellyfin:
    image: jellyfin/jellyfin:latest
    container_name: jellyfin
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.jellyfin.rule=Host(`jellyfin.yourdomain.com`)"
      - "traefik.http.routers.jellyfin.tls.certresolver=letsencrypt"
      - "traefik.http.services.jellyfin.loadbalancer.server.port=8096"

When this container starts, Traefik automatically detects it and creates the route. When it stops, the route disappears. No manual intervention.

The Dashboard

Traefik includes a built-in dashboard showing all routers, services, and middleware. It’s read-only but incredibly useful for debugging.

Pros

  • Auto-discovery — detects Docker containers automatically
  • No config changes — labels on containers handle everything
  • Built-in dashboard — visualize your routing setup
  • Middleware system — powerful chain of request/response processing
  • Kubernetes support — scales from homelab to production

Cons

  • Steeper learning curve — label syntax is verbose and easy to get wrong
  • Docker socket access — requires mounting the Docker socket (security concern)
  • Verbose configuration — initial setup has more moving parts
  • Debugging — when labels are wrong, errors can be cryptic
  • Overkill for simple setups — lots of power you may not need

Head-to-Head Comparison

Ease of Setup

  1. Nginx Proxy Manager — deploy, open browser, click buttons
  2. Caddy — write 3 lines per service, deploy
  3. Traefik — longer initial config, but adding services is effortless after

SSL Experience

  1. Caddy — literally automatic, zero configuration
  2. Nginx Proxy Manager — checkbox in the GUI
  3. Traefik — requires certificate resolver setup

Adding New Services

  1. Traefik — add labels to docker-compose, done
  2. Caddy — add 3 lines to Caddyfile, reload
  3. Nginx Proxy Manager — open GUI, fill out form, save

Resource Usage

All three are lightweight. In typical homelab usage:

  • Caddy: ~15-30MB RAM
  • Traefik: ~30-50MB RAM
  • Nginx Proxy Manager: ~50-100MB RAM (includes the GUI)

When Things Break

  • Caddy: clear error messages, excellent docs
  • Nginx Proxy Manager: GUI shows status, Nginx logs available
  • Traefik: dashboard helps, but label typos can be silent failures

Which Should You Choose?

Choose Nginx Proxy Manager if:

  • You’re brand new to self-hosting
  • You prefer clicking over typing
  • You want the fastest path to “it works”
  • You don’t mind manually adding each service

Choose Caddy if:

  • You’re comfortable editing text files
  • You want the cleanest, most maintainable config
  • You value simplicity and reliability
  • You want HTTP/3 and modern TLS defaults

Choose Traefik if:

  • You run many Docker containers and add new ones often
  • You want zero-touch service discovery
  • You plan to scale beyond a single server
  • You’re comfortable with a steeper initial learning curve

My Recommendation for Beginners

Start with Caddy.

Here’s why: Nginx Proxy Manager is easier on day one, but Caddy is easier on day thirty. The Caddyfile is so simple that you’ll understand your entire setup at a glance. When something breaks, you’ll know exactly where to look.

If you’re running 10+ containers and constantly spinning up new ones, switch to Traefik later. The auto-discovery is genuinely magical once it’s set up.

And if you truly want zero command line, Nginx Proxy Manager will serve you well. There’s no shame in a GUI — it works great.

Migration Tips

Already using one and want to switch? The process is straightforward:

  1. Document your current routes — list every domain → service mapping
  2. Set up the new proxy alongside the old one (on different ports)
  3. Test each route using the new proxy
  4. Switch ports — stop the old proxy, change the new one to 80/443
  5. Verify SSL — certificates may need to be re-issued

All three use Let’s Encrypt, so certificate migration isn’t necessary — just let the new proxy request fresh ones.

Conclusion

There’s no wrong choice here. All three reverse proxies are reliable, well-maintained, and capable of handling a homelab. The “best” one is whichever matches your comfort level and workflow.

Start simple. You can always switch later. The important thing is getting your services behind HTTPS and accessible from clean URLs — any of these three will get you there.