2026-06-19 · 7 min
nginx vs Traefik vs Caddy: 6 Bulan di VPS Jakarta
Tiga klien VPS Jakarta saya operate dengan reverse proxy berbeda — bukan disengaja, kebetulan masing-masing punya legacy yang berbeda. Setelah 6 bulan saya catat metric: latency, memory, ops time, dan insiden. Comparison apple-to-apple sebisa mungkin.
Setup
Klien A: nginx 1.27 di Hetzner cx41 (Ubuntu 24.04). 4 backend service (Bun + Hono). Static site juga dari nginx.
Klien B: Traefik 3.2 di Hetzner cx51 dengan Docker Compose. 6 backend service.
Klien C: Caddy 2.8 di Hetzner cx41. 3 backend service (Bun apps).
Semua: SSL via Let’s Encrypt. Same region (FSN1, Falkenstein), serving Indonesia user via Cloudflare CDN front-most cases.
Latency overhead reverse proxy
Saya benchmark dengan wrk -t 4 -c 100 -d 60s ke endpoint health check (echo back). Compare langsung-ke-backend vs lewat proxy:
nginx:
- Direct backend: P50 1.8ms, P95 3.2ms
- Via nginx: P50 2.1ms, P95 3.8ms
- Overhead: ~300μs P50, ~600μs P95
Traefik:
- Direct backend: P50 1.9ms, P95 3.4ms
- Via Traefik: P50 2.6ms, P95 5.2ms
- Overhead: ~700μs P50, ~1.8ms P95
Caddy:
- Direct backend: P50 1.7ms, P95 3.0ms
- Via Caddy: P50 2.0ms, P95 3.6ms
- Overhead: ~300μs P50, ~600μs P95
nginx dan Caddy tie. Traefik 2x lebih banyak overhead. Source: Traefik lebih banyak feature middleware di hot path (metrics, tracing, dashboard scrape).
Untuk SMB workload: 1ms beda tidak material. Untuk high-throughput trading: bisa relevant.
Memory footprint
Memory baseline RSS process:
- nginx: 24MB (1 master + 4 worker)
- Traefik: 118MB (single binary + Docker provider polling)
- Caddy: 78MB (Go runtime overhead)
Untuk VPS dengan budget tight (cx21 4GB RAM): nginx clear winner. Caddy still OK. Traefik bisa 3% RAM usage di small VPS.
SSL & cert automation
Caddy: out-of-box otomatic HTTPS. Tulis domain di Caddyfile, cert otomatis provisioned & renewed.
api.example.com {
reverse_proxy localhost:3000
}
Zero config beyond domain. Saya prefer Caddy untuk demo / staging environment yang sering ganti domain.
Traefik: support Let’s Encrypt via config provider. Setup straightforward di Docker Compose:
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls.certresolver=letsencrypt"
nginx: butuh certbot external. Renewal via cron. Setup 30-60 menit per domain pertama, tapi solid sekali jalan.
Untuk klien dengan ops tim familiar nginx: cron-renewal pattern OK. Untuk DevX cepat: Caddy menang besar.
Config experience
nginx:
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
location / {
proxy_pass http://localhost:3000;
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;
}
}
Verbose tapi explicit. Tiap parameter searchable di Stack Overflow.
Caddy:
api.example.com {
reverse_proxy localhost:3000
}
Sangat concise. Defaults sensible (HTTP/2, HSTS, security header).
Traefik:
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`api.example.com`)"
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls.certresolver=letsencrypt"
- "traefik.http.services.api.loadbalancer.server.port=3000"
Verbose di Docker label. Tapi dynamic — restart container, route otomatis update. Sangat cocok untuk Docker Compose stack.
Insiden 6 bulan
Klien A (nginx):
- 0 insiden langsung dari nginx
- 1x bad config typo saat saya add new route → nginx -t catch sebelum reload. Tidak ada downtime.
Klien B (Traefik):
- 1 insiden: Traefik 3.0 → 3.1 upgrade ada breaking change di label syntax. Saya tidak baca changelog cukup teliti. 8 menit downtime saat rolling restart.
- 1 insiden: Docker provider polling stuck karena Docker socket overloaded (banyak container restart). Traefik tidak detect new container. Restart Traefik fix it.
Klien C (Caddy):
- 1 insiden: Let’s Encrypt rate limit hit saat klien add 50 subdomain dalam satu hari. Caddy retry exponentially backoff, semua subdomain provisioned dalam 24 jam. Tidak ada downtime tapi delay.
- 0 insiden lain.
DX di hot reload / config change
nginx: nginx -t && nginx -s reload. Saya pakai systemctl reload nginx. 0 downtime, instant reload.
Caddy: caddy reload --config /etc/caddy/Caddyfile. 0 downtime. Atau API call ke /load endpoint.
Traefik: Docker label change → Traefik detect automatically. Sangat nice. Restart container = route update tanpa intervention.
Untuk Docker-heavy environment: Traefik menang DX. Untuk single-binary VPS deployment: Caddy seedikit lebih nyaman karena single config file (vs nginx multi-file include).
Observability
nginx: access log + error log file. Untuk metric: butuh nginx-vts module atau Prometheus exporter terpisah.
Caddy: structured JSON log built-in. Prometheus metric di /metrics endpoint.
Traefik: dashboard UI built-in (bagus untuk overview), Prometheus + Jaeger tracing integration native.
Untuk debug “kenapa request gagal”: Traefik dashboard sangat helpful. Untuk production observability dengan Grafana: ketiga-nya bisa, tapi Caddy paling lean setup.
Yang saya pakai untuk diri sendiri
Untuk project saya pribadi (kodekarawaci, serpongtech personal site, dll): Caddy. Auto-HTTPS + concise config. Setup 5 menit per domain.
Untuk klien yang sudah ada legacy nginx setup matang: tetap nginx. Tidak ada reason migrate.
Untuk klien dengan Docker Compose stack 10+ container: Traefik. Label-based routing scale jauh lebih nyaman.
Verdict
- VPS small budget + max stability: nginx. Memory paling kecil, ekosistem paling matang.
- Greenfield project / personal dev: Caddy. DX terbaik, auto-HTTPS.
- Docker Compose / Swarm stack: Traefik. Native integration worth overhead.
Bukan magic — semua 3 production-grade. Pilih based on existing infra + tim familiarity. Saya tidak migrate klien dari satu ke lainnya tanpa compelling reason; switching cost > marginal improvement.
Pattern serupa dengan systemd vs Docker deployment saya tulis sebelumnya: pilih tools yang fit infrastructure existing, bukan default ke yang trendy.
Ditulis oleh Reza Pradipta