Traefik, Caddy or NGINX Proxy Manager: which reverse proxy for a few Docker containers
Practical comparison of the three most popular reverse proxies on a small Docker VPS: certificates, interface, RAM consumption and minimal configuration for each.
UptimeMag editorial team · 18 October 2026 · 4 min read

Anyone running a VPS with four or five Docker containers eventually asks the same question: how do I get HTTPS traffic to the right containers without manually rewriting a certificate every three months? The options that always come up are three: Traefik, Caddy and NGINX Proxy Manager (NPM). They do the same job — terminate TLS and route requests — but start from different assumptions, and on a small VPS the difference is felt most acutely in free RAM and the time it takes to add a service.
Automatic certificates: who does what
All three obtain and renew Let's Encrypt certificates automatically, so on this point there's no loser. The mechanism differs. Traefik uses ACME resolvers configured in the static file and applies them to routers via labels. Caddy has automatic HTTPS as default behaviour: just write the domain name in the Caddyfile, without declaring anything about the certificate, and it starts automatically. By default, Caddy provisions and renews TLS certificates for domains and redirects HTTP to HTTPS; writing "example.com { reverse_proxy localhost:3000 }" automatically enables HTTPS with Let's Encrypt certificates. NGINX Proxy Manager instead requires you to go into the web interface, enter the domain and tick the certificate request: same final automation, one extra manual step the first time.
Graphical interface or files only
Here the three paths truly diverge. Caddy has the most compact configuration with a Caddyfile or API, NGINX Proxy Manager uses a web interface built on top of Nginx, Traefik has static configuration plus dynamic providers. In practice: with NPM you click, with Caddy you write a short readable text file that someone else can understand, with Traefik you add labels to the container block in docker-compose.yml and routing follows the container automatically when it starts or stops. Traefik keeps routing details alongside the services they belong to, an advantage you notice when managing more than a couple of containers.
What they actually consume
RAM numbers at rest vary from source to source depending on version, operating system and measurement method, so they should be read as an order of magnitude, not as absolute values to put on a spec sheet. The most transparent measurement we found, with method fully disclosed, is from Virtua Cloud, which used docker stats --no-stream on a 4 vCPU, 8 GB RAM VPS with Ubuntu 24.04, with each proxy at rest and no traffic:
| Proxy | RAM at rest (measured) | Image size |
|---|---|---|
| Traefik v3.6 | ~17 MB | ~242 MB |
| Caddy 2.11 | ~14 MB | ~88 MB |
| Nginx 1.28 (base, without NPM) | ~5 MB | ~240 MB |
The numbers come from docker stats --no-stream on a Virtua Cloud VPS with 4 vCPU/8 GB RAM running Ubuntu 24.04, with each proxy at rest and no traffic. Other measurements published online report higher figures for all three, up to 200 MB for Traefik and for full NPM with Node.js interface, a sign that consumption depends heavily on how many routers and middleware are active and how many hosts are configured in the panel. The consistent pattern across multiple independent sources is the relative order: Caddy and Traefik stay lightweight as long as only a few sites are configured, NPM weighs more because the Node.js interface backend also runs on top of Nginx.
Minimal example — Traefik
services:
traefik:
image: traefik:v3.6
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.le.acme.email=your@domain.it"
- "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./letsencrypt:/letsencrypt"
app:
image: myapp:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`app.yourdomain.it`)"
- "traefik.http.routers.app.entrypoints=websecure"
- "traefik.http.routers.app.tls.certresolver=le"
Minimal example — Caddy
services:
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:
Caddyfile:
app.yourdomain.it {
reverse_proxy app:3000
}
Configuration is loaded at startup and Caddy supports graceful reload, without interrupting connections, with "docker compose exec -w /etc/caddy caddy caddy reload", so adding a site doesn't require restarting the container.
Minimal example — NGINX Proxy Manager
services:
app:
image: 'jc21/nginx-proxy-manager:2.16.0'
restart: unless-stopped
ports:
- '80:80'
- '443:443'
- '81:81'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
The information about image version and port 81 for the administration interface comes from the official setup instructions, which create a docker-compose.yml with the image 'jc21/nginx-proxy-manager:2.16.0' and ports 80, 443 and 81 exposed. After first startup you create the administrator user from the interface at http://server-ip:81, then each domain and certificate are added from there without touching files.
Which to choose for a VPS with few containers
For 2-5 containers on a small VPS (1-2 GB RAM), none of the three tools is heavy enough to decide the choice. What matters more is your working style: if containers appear and disappear often and you prefer not to touch files, Traefik with labels reads configuration directly from docker-compose.yml. If you prefer a short text file, versionable in git, without an interface to open, Caddy is the most straightforward option. If you don't want to write anything by hand and accept an extra web interface to keep updated, NGINX Proxy Manager remains the most direct path for someone starting from scratch.
Written with the help of artificial intelligence and checked by the editors (EU AI Act, art. 50).