karawaci.kode

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