Health Check
The endpoint everyone thinks is trivial — and which, badly designed, breaks the auto-healing infrastructure. Liveness, readiness, startup: three different questions.
Problem
An EDI service starts by loading 200 MB of EDIFACT schemas,
establishes a Kafka and Redis connection. During this 15s warmup,
Kubernetes sends a GET / returning 200: the Pod is
marked Ready and starts receiving traffic. The first requests
crash with NPE "schemas not loaded". Worse: in production, a
corrupted schema crashes the parser on every request, but the
process stays alive — Kubernetes never kills the Pod because /health always returns 200. The Pod is a zombie.
Forces
- Several different questions: is the process running? Can it accept traffic now? Has it finished booting?
- The orchestrator reacts differently per answer. Liveness fail → restart Pod. Readiness fail → remove from LB. Startup fail → wait longer.
- Health must be cheap. Polled every 5-10s per Pod. No heavy DB query.
- Health can lie. If we test "can I ping Kafka", a downstream partner down makes us say ready=false. Distinguish critical vs optional dependency.
Solution
Expose three distinct endpoints: /health/live (the process is alive —
minimal test, touches no dependency, just "I can answer HTTP"), /health/ready (ready to receive
traffic — check critical dependencies: DB, broker, schemas loaded), /health/startup (boot complete —
useful for slow-starting apps). Response format: follow the IETF
Health Check Response Format draft or Microsoft Health Endpoint.
On Kubernetes: configure livenessProbe, readinessProbe, startupProbe with right
parameters (periodSeconds, failureThreshold).
Three distinct endpoints (RFC Health Check Response Format):
GET /health/live → liveness
200 = "process alive, do not kill"
503 = "process zombie, restart"
GET /health/ready → readiness
200 = "ready to receive traffic"
503 = "warmup / dependency down / drain"
→ on 503, LB removes the Pod from round-robin
GET /health/startup → startup
200 = "initialisation complete"
503 = "still loading EDIFACT schemas"
→ blocks other probes during boot
RFC JSON response:
{
"status": "pass" | "warn" | "fail",
"version": "1.4.2",
"checks": {
"kafka": { "status": "pass", "time": "2026-05-16T08:30:00Z" },
"redis": { "status": "pass" },
"as2.walmart": { "status": "warn", "observedValue": 12s }
}
}
EDI implementation
Concrete case: EDIFACT parser service in Spring Boot with Spring
Boot Actuator. /actuator/health/liveness returns 200
except if the process is in a non-recoverable state
(OutOfMemoryError, detected deadlock). /actuator/health/readiness checks: Kafka producer
connection, Redis cache connection, EDIFACT schemas
D.96A/D.01B/D.24A loaded, Circuit Breaker state to Walmart (if
open for > 30 min → warn, else pass). /actuator/health/startup returns 503 until schemas
are loaded (up to 90s). On Kubernetes, startupProbe.failureThreshold=30 × periodSeconds=5s =
2.5 min grace period before abandon. For PEPPOL Access Point,
health must also check AS4 certificate rotation — an expired cert
makes the AP unusable even if the process runs.
Anti-patterns
- One single
/health. Mixing liveness and readiness means an external dependency down restarts the Pod in a loop (which helps nothing). - Health that runs business logic. "I test by sending a real INVOIC to Walmart": pollutes the partner audit log, may breach SLA.
- Health hidden behind auth. The kubelet cannot easily authenticate. Expose with internal network policy without auth.
- Health that queries every dependency in cascade. If one dependency check takes 30s, the health times out, the Pod is wrongly restarted.
- Readiness cascade. Service A ready=false if service B ready=false: a failure propagates backwards. Distinguish critical dependency (fail if missing) vs graceful degradation.
Related patterns
- Circuit Breaker — breaker state can influence readiness.
- Circuit Breaker Half-Open — the test request can be a health check.
- Control Bus — the admin channel can consume health events.
- Test Message — the synthetic canary is a business health check.
- Wire Tap — passive observability, complementary to active health.
Sources
- Microsoft Architecture Center — Health Endpoint Monitoring pattern. learn.microsoft.com — Health endpoint
- Kubernetes docs — Configure Liveness, Readiness and Startup Probes. kubernetes.io — Probes
- IETF draft — Health Check Response Format for HTTP APIs (Nottingham, Carrasco). Canonical JSON format. ietf.org — API health check
- Spring Boot Actuator — HealthIndicator. The idiomatic Java implementation. spring.io — Actuator health
- Newman S. — Building Microservices, O'Reilly 2nd ed. 2021. Chap. "Monitoring" develops the differences between liveness and readiness.