Comparison10 min read

Caddy, Traefik or Nginx Proxy Manager: which to choose?

You host several Docker applications on a VPS and need to expose each one over HTTPS with a separate domain name. A reverse proxy is the central piece of this architecture: it receives all incoming traffic on ports 80 and 443, handles TLS certificates and routes requests to the right container. Caddy, Traefik and Nginx Proxy Manager are the three most popular solutions — this guide helps you choose the one that matches your profile.

Why a reverse proxy is essential with Docker

By default, each Docker container listens on an arbitrary port: your Nextcloud is on port 8080, your Gitea on 3000, your Vaultwarden on 8200. You cannot directly expose a dozen ports in production — browsers do not accept URLs with port numbers, TLS certificates cover domain names not ports, and your firewall must stay closed on everything except 80 and 443. A reverse proxy solves all three problems at once: it becomes the single public entry point, routes traffic based on hostname or path, and handles TLS termination. The result is that app1.your-domain.com and app2.your-domain.com both point to port 443 of your VPS, and the proxy knows which container to forward each request to.

What a reverse proxy gives you in practice

  • Centralized TLS termination — a single tool obtains and renews Let's Encrypt certificates for all your domains automatically without any manual steps
  • Hostname-based routinggit.your-domain.com goes to Gitea, cloud.your-domain.com goes to Nextcloud with no port conflicts whatsoever
  • Simplified firewall — only ports 80 and 443 are open to the public, all internal Docker ports remain inaccessible from the outside
  • Automatic HTTP → HTTPS redirect — all unencrypted traffic is redirected with a 301 without manual configuration inside each application
  • Centralized observability — access and error logs from all your applications are aggregated in one place to simplify debugging

Common prerequisites for all three solutions

Before choosing and installing one of these three proxies, make sure your VPS meets a few basic requirements. Docker and Docker Compose must be installed (Compose V2 recommended, meaning the docker compose command without a hyphen). Your DNS records must point to your VPS public IP before requesting a certificate — Let's Encrypt verifies DNS during HTTP-01 validation, and an error at this stage can trigger a rate limit. Ports 80 and 443 on your VPS must be free: check with ss -tlnp | grep -E ':80|:443'. Finally, create a dedicated Docker network — docker network create proxy.

Comparison table: Caddy vs Traefik vs Nginx Proxy Manager

CriterionCaddyTraefikNginx Proxy Manager
Setup ease⭐⭐⭐⭐⭐ Very simple⭐⭐⭐ Medium⭐⭐⭐⭐⭐ Very simple
Automatic TLS (Let's Encrypt)✅ Native, zero config✅ Via ACME✅ Via web interface
Docker integrationManual (labels or config)✅ Native labels✅ Via GUI
Configuration methodReadable CaddyfileDocker labels + YAMLGraphical web interface
Zero-downtime reload✅ Automatic✅ Automatic✅ Automatic
Web interface/dashboard❌ No✅ Dashboard included✅ Full interface
Memory usage~30 MB~50 MB~100 MB (MariaDB included)
Best forSolo projects/small teamsMicroservices, CI/CDBeginners, mixed teams

Caddy: the default choice for most use cases

Caddy has established itself as the simplest choice for developers hosting between two and ten applications on a VPS. Its philosophy is radical: TLS is enabled by default for any valid domain, with no option to tick and no variable to set. The Caddyfile configuration file is intentionally readable, close to natural language, and can fit in about ten lines for a typical use case. Caddy is written in Go and embeds its own ACME client: it contacts Let's Encrypt directly, stores certificates in its data directory and renews them automatically before expiry. Its memory footprint stays around 30 MB at rest, making it perfectly suited to entry-level VPS instances.

Deploy Caddy as a Docker reverse proxy

01

Create the shared Docker network

First, create the network that Caddy and your applications will share: docker network create proxy. This isolated network lets Caddy reach your containers by name without exposing their ports on the host.

02

Write the Caddyfile

Create a Caddyfile at the root of your project. To expose an application at app.your-domain.com to a container named myapp listening on port 3000: app.your-domain.com { reverse_proxy myapp:3000 }. Caddy obtains the certificate automatically on first start.

03

Write the docker-compose.yml for Caddy

Create a docker-compose.yml with the Caddy service: mount the Caddyfile read-only (./Caddyfile:/etc/caddy/Caddyfile:ro), persist TLS data in a named volume (caddy_data:/data), publish ports 80 and 443, and attach Caddy to the proxy network declared as external.

04

Attach your applications to the proxy network

In each application's docker-compose.yml, add the proxy network as an external network and stop exposing ports on the host (use expose instead of ports). Caddy will reach the container via the internal Docker network.

05

Start and verify

Launch Caddy with docker compose up -d, then follow logs with docker compose logs -f caddy. You should see certificate obtained successfully for each domain. Test with curl -I https://app.your-domain.com.

06

Add a new application

For each new application, add a block to the Caddyfile, reload Caddy without downtime with docker exec caddy caddy reload --config /etc/caddy/Caddyfile, and attach the new container to the proxy network.

Traefik: when to choose automatic service discovery

Traefik shines in contexts where the number of services changes frequently: CI/CD environments that create and destroy containers on each deployment, microservice architectures with more than five independent services, or teams where each developer deploys their own stacks without touching a centralized configuration. Its automatic discovery mechanism via Docker labels is its main strength: when you start a container with the right labels, Traefik detects it instantly and creates the route without you touching Traefik's own configuration. Traefik also includes a web dashboard that visualizes all active routers, services and middlewares in real time.

Deploy Traefik with automatic Docker discovery

01

Create the static configuration file

Create traefik.yml with entrypoints (web on port 80, websecure on 443), enable the Docker provider (docker: { exposedByDefault: false }), configure the ACME resolver with your email for Let's Encrypt, and enable the dashboard in secure mode.

02

Launch Traefik with Docker Compose

In Traefik's docker-compose.yml, mount the Docker socket read-only (/var/run/docker.sock:/var/run/docker.sock:ro), mount traefik.yml, persist certificates in a volume, and publish ports 80 and 443. Start with docker compose up -d.

03

Annotate your containers with labels

On each service to expose, add Traefik labels: traefik.enable=true, the router rule (traefik.http.routers.myapp.rule=Host('app.your-domain.com')), the entrypoint (websecure), the TLS resolver and the internal service port.

04

Verify in the dashboard

Access the Traefik dashboard and verify that your router appears in green with Enabled status. If the router is missing, check that the traefik.enable=true label is present and the container is attached to the network Traefik is watching.

Critical security: never mount the Docker socket (/var/run/docker.sock) without restriction in a multi-user environment — anyone who can write to this socket can take full control of the host. In production, prefer the socket read-only (ro) or use a socket proxy such as docker-socket-proxy. Also apply a basic authentication middleware to the Traefik dashboard before exposing it publicly.

Nginx Proxy Manager: the no-config-file option

Nginx Proxy Manager (NPM) is the obvious choice for anyone uncomfortable with command-line configuration files. Its web interface allows you to create a proxy host in a few clicks: enter the domain name, the target container address, check 'Force SSL' and 'HTTP/2 Support', click 'Save' — that is it. NPM handles the rest, including the Let's Encrypt certificate request. However, NPM embeds a MariaDB database to store its configuration, which pushes its memory footprint to around 100 MB — nearly triple that of Caddy.

Troubleshooting: the most common errors

The vast majority of problems fall into four categories. First, Let's Encrypt rate limits: if you restart your stack several times in testing with the same domain, you can exhaust the limit of five certificates per domain over seven days. Solution: use the Let's Encrypt staging environment for your tests. Second, port conflicts: if port 80 or 443 is already occupied by the host's Apache or Nginx, the Docker proxy will not start. Identify the process with ss -tlnp | grep :80. Third, Docker network errors: verify both containers are on the same Docker network with docker network inspect proxy. Fourth, for Traefik, typos in labels are the most common cause of silently missing routes — enable debug logging with --log.level=DEBUG.

Summary: which proxy for which profile

  • Solo developer, 2 to 5 apps — Caddy: minimal configuration, automatic TLS, up and running in under ten minutes with a fifteen-line Caddyfile
  • DevOps team, microservices, CI/CD — Traefik: automatic discovery via labels, monitoring dashboard, perfect when the number of services varies dynamically
  • Non-technical user or mixed team — Nginx Proxy Manager: intuitive graphical interface, no files to edit, built-in multi-user management
  • VPS with limited RAM (512 MB to 1 GB) — Caddy or Traefik: avoid NPM which bundles MariaDB and consumes significantly more memory
  • Progressive migration from Nginx — Caddy: its syntax can be learned in an hour, and it can temporarily coexist with Nginx on different ports

Conclusion: start with Caddy, evolve if needed

For the vast majority of self-hosters managing a few Docker applications on a VPS, Caddy is an excellent starting point: it is simple, lightweight, opinionated in the right way and handles TLS better than any alternative without extra configuration. If your infrastructure grows beyond five services with frequent automated deployments, Traefik becomes more appropriate thanks to its dynamic discovery. If you need to delegate domain management to non-technical people, Nginx Proxy Manager is the only truly accessible option.

Your Docker-ready VPS in under 5 minutes

ServOrbit VPS are delivered with Docker pre-installed, a dedicated public IP and a 1 Gbps network. Deploy Caddy, Traefik or Nginx Proxy Manager immediately after delivery, with no additional network configuration.

Need help?

Browse our help center and FAQ, or reach our team — callback, WhatsApp or email. Support in French, English and Arabic.