Passive Health Check #
Passive health check adalah mekanisme di mana Caddy mengamati hasil dari request nyata yang dikirim ke backend — tanpa mengirim request health check tambahan. Ketika Caddy melihat backend gagal menjawab (koneksi ditolak, timeout, atau error) dalam batas tertentu, ia secara otomatis menandai backend tersebut sebagai unhealthy dan berhenti mengirim traffic ke sana.
Berbeda dari active health check yang “aktif bertanya”, passive health check “pasif mengamati”. Keduanya melengkapi satu sama lain dan bisa dikonfigurasi bersamaan untuk proteksi berlapis.
Cara Kerja Passive Health Check #
Caddy mengamati setiap response dari backend:
Request 1 → backend-2 → 500 Internal Server Error ← Catat kegagalan (1/3)
Request 2 → backend-2 → Connection refused ← Catat kegagalan (2/3)
Request 3 → backend-2 → Timeout setelah 30s ← Catat kegagalan (3/3)
Threshold 3 tercapai! → backend-2 ditandai UNHEALTHY
Traffic tidak lagi dikirim ke backend-2
[Jika passive saja, tidak ada check aktif]
[Caddy tetap sesekali mencoba backend-2 untuk recovery]
Request nyata dikirim ke backend-2 → 200 OK ← Catat sukses (1/2)
Request nyata dikirim ke backend-2 → 200 OK ← Catat sukses (2/2)
Threshold recovery 2 tercapai! → backend-2 kembali HEALTHY
Konfigurasi Passive Health Check #
example.com {
reverse_proxy backend-1:3000 backend-2:3000 backend-3:3000 {
# Tandai unhealthy setelah N kegagalan berturut-turut
health_fails 3
# Tandai kembali healthy setelah N sukses berturut-turut
health_passes 2
}
}
Kegagalan yang Dihitung #
Yang DIHITUNG sebagai kegagalan:
✓ Connection refused (backend tidak bisa dijangkau)
✓ Connection timeout (backend tidak respond dalam batas waktu)
✓ Network error (DNS tidak resolve, interface down, dll.)
✓ Status code yang dianggap error (5xx secara default)
Yang TIDAK dihitung sebagai kegagalan:
✗ Response 4xx dari backend (itu adalah response valid dari backend)
✗ Request yang di-cancel oleh client sebelum backend respond
✗ Health check requests (passive health check tidak menghitung health check)
max_fails dan fail_duration — Window-Based Failure Counting
#
Selain menghitung kegagalan berurutan (health_fails), Caddy mendukung window-based failure counting yang lebih nuanced:
example.com {
reverse_proxy backend-1:3000 backend-2:3000 backend-3:3000 {
# Tandai unhealthy jika ada lebih dari N kegagalan
# dalam window waktu tertentu
# (ini adalah konfigurasi lebih lanjut via JSON config)
# Untuk Caddyfile, gunakan:
health_fails 5 # Maksimum 5 kegagalan berturut-turut sebelum unhealthy
health_passes 3 # Butuh 3 sukses berturut-turut untuk recovery
}
}
Passive vs Active: Kapan Menggunakan Mana #
Active Health Check:
+ Deteksi kegagalan SEBELUM user terpengaruh
+ Backend down dideteksi dalam 1 interval (misal 10s)
+ Tidak ada user yang mengalami error akibat backend down
- Menambah traffic ke backend (health check requests)
- Perlu endpoint /health di backend
- Overhead kecil untuk koneksi + request health check
Passive Health Check:
+ Tidak ada overhead request tambahan
+ Tidak perlu endpoint /health khusus di backend
+ Deteksi otomatis berbasis traffic nyata
- User AKAN mengalami beberapa error sebelum backend ditandai unhealthy
- Jika traffic rendah, deteksi butuh waktu lebih lama
- Backend yang sangat jarang menerima traffic mungkin tidak pernah dideteksi
Kombinasi keduanya (TERBAIK untuk production):
Active → Deteksi proaktif, user tidak terpengaruh
Passive → Safety net tambahan untuk kondisi yang mungkin lolos active check
Kombinasi Active + Passive Health Check #
Ini adalah konfigurasi yang direkomendasikan untuk production:
{
email [email protected]
}
api.example.com {
reverse_proxy backend-1:8080 backend-2:8080 backend-3:8080 {
lb_policy least_conn
# ── Active Health Check ──────────────────────────────────
health_uri /health # Endpoint yang dicek aktif
health_interval 10s # Cek setiap 10 detik
health_timeout 5s # Timeout per check
health_status 200 # Kode yang dianggap sehat
# ── Passive Health Check ─────────────────────────────────
health_fails 3 # Unhealthy setelah 3 error berurutan
health_passes 2 # Recovery setelah 2 sukses berurutan
# Header forwarding
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Proto {scheme}
# Transport
transport http {
dial_timeout 5s
response_header_timeout 30s
}
# Retry ke upstream lain jika gagal
lb_try_duration 5s
lb_try_interval 200ms
}
}
Retry Logic — Melengkapi Health Check #
Retry memungkinkan Caddy mencoba upstream lain ketika request pertama gagal, SEBELUM passive health check threshold tercapai:
example.com {
reverse_proxy backend-1:3000 backend-2:3000 backend-3:3000 {
health_fails 3
health_passes 2
# Jika request ke backend pertama gagal (koneksi error),
# coba backend lain dalam waktu maksimal 5 detik
lb_try_duration 5s # Total waktu untuk semua retry
lb_try_interval 250ms # Jeda antara percobaan
# Retry hanya terjadi untuk kegagalan koneksi (sebelum response diterima)
# Tidak retry jika backend sudah return response (misal: 500 error)
}
}
Scenario: backend-2 down, request dikirim ke backend-2
Tanpa retry:
Request → backend-2 → Connection refused → 502 Bad Gateway ke user
(user langsung dapat error, passive health check catat +1 failure)
Dengan retry (lb_try_duration 5s):
T=0ms Request → backend-2 → Connection refused (failure!)
T=0ms Caddy retry → backend-3 → 200 OK → user dapat response normal
(user tidak mengalami error sama sekali)
(passive health check tetap catat failure untuk backend-2)
(setelah N failures, backend-2 ditandai unhealthy)
Circuit Breaker Pattern dengan Passive Health Check #
Passive health check dalam Caddy mengimplementasikan pola circuit breaker secara implisit:
State: CLOSED (normal)
Traffic mengalir ke semua backend
Passive health check mengamati error
State: OPEN (backend unhealthy)
Backend X tandai unhealthy setelah threshold failures
Traffic di-redirect ke backend lain
Backend X tidak menerima traffic baru
State: HALF-OPEN (percobaan recovery)
Caddy sesekali kirim request ke backend X
(bergantung pada traffic yang masuk dan rotation)
Jika N sukses berurutan → kembali CLOSED
Jika masih gagal → tetap OPEN
Implementasi di Caddy:
CLOSED → health_fails threshold belum tercapai
OPEN → health_fails threshold tercapai, backend unhealthy
HALF-OPEN → otomatis: backend di-coba lagi saat ada traffic
(tidak perlu konfigurasi terpisah)
Konfigurasi untuk Berbagai Skenario #
Toleransi Tinggi (Loose) #
Untuk service yang sesekali error tapi tidak kritis:
example.com {
reverse_proxy backend-1:3000 backend-2:3000 {
# Butuh lebih banyak kegagalan sebelum unhealthy
health_fails 10
health_passes 5
# Tidak ada active health check
# Hemat resource untuk service non-kritis
}
}
Toleransi Rendah (Strict) #
Untuk service kritis yang tidak boleh ada error sama sekali:
critical.example.com {
reverse_proxy backend-1:3000 backend-2:3000 backend-3:3000 {
# Active health check yang agresif
health_uri /health
health_interval 5s # Check lebih sering
health_timeout 3s # Timeout lebih ketat
# Passive: fail cepat
health_fails 1 # Langsung unhealthy setelah 1 kegagalan
health_passes 3 # Butuh 3 sukses untuk recovery (lebih konservatif)
# Retry agresif
lb_try_duration 3s
lb_try_interval 100ms
}
}
Logging dan Observability #
Passive health check events bisa diamati dari log Caddy:
# Filter log untuk health check events
sudo journalctl -u caddy | grep -i "health\|unhealthy\|upstream"
# Output contoh:
# INFO http.handlers.reverse_proxy upstream is now unavailable
# {"upstream": "backend-2:3000", "total_fails": 3}
# INFO http.handlers.reverse_proxy upstream is now available
# {"upstream": "backend-2:3000"}
# Dengan JSON log, bisa di-filter dengan jq
tail -f /var/log/caddy/access.log | jq 'select(.upstream_addr != null) | {
time: .ts,
upstream: .upstream_addr,
status: .status,
latency: .duration
}'
# Hitung error rate per upstream (dari access log)
cat /var/log/caddy/access.log | jq -r \
'[.upstream_addr, (.status | tostring)] | @tsv' | \
awk -F'\t' '{
total[$1]++
if ($2 >= "500") errors[$1]++
} END {
for (addr in total) {
err_rate = (errors[addr]+0) / total[addr] * 100
printf "%s: %d total, %.1f%% errors\n", addr, total[addr], err_rate
}
}'
Ringkasan #
- Passive health check mengamati error dari request nyata — tanpa mengirim request tambahan, tanpa membutuhkan endpoint
/healthdi backend.- Konfigurasi inti:
health_fails N(unhealthy setelah N kegagalan berurutan) danhealth_passes N(recovery setelah N sukses berurutan).- Passive saja tidak cukup: user akan mengalami beberapa error sebelum backend ditandai unhealthy. Selalu kombinasikan dengan active health check untuk production.
- Retry (
lb_try_duration) bekerja sinergis dengan passive health check — request pertama dicoba ke backend lain sebelum user merasakan error, sambil tetap mencatat kegagalan untuk passive health check.- Pola circuit breaker diimplementasikan secara implisit: CLOSED (normal) → OPEN (unhealthy) → HALF-OPEN (recovery otomatis) tanpa konfigurasi tambahan.
- Untuk service kritis, gunakan
health_fails 1agar satu kegagalan langsung menyebabkan backend dikeluarkan dari rotasi — kombinasikan dengan lebih banyak backend untuk redundansi.
← Sebelumnya: Active Health Check Berikutnya: Admin Endpoint →