Health Checks & Readiness Probes
IntermediateLiveness probes detect deadlocked processes for restart; readiness probes tell the load balancer when a newly started instance is ready to accept traffic.
Overview
Kubernetes uses three probe types to manage container health and traffic routing. Liveness probes detect when a container is deadlocked or irrecoverably broken — K8s restarts it. Readiness probes detect when a container is temporarily unable to serve traffic (warming up, DB connection slow, circuit breaker open) — K8s removes it from the Service's load balancer endpoints without restarting it. Startup probes protect slow-starting containers during the initial boot period. Implementing these probes correctly is one of the most important reliability concerns in a microservices deployment. Spring Boot Actuator (2.3+) exposes /actuator/health/liveness and /actuator/health/readiness out of the box, with fine-grained control over which health indicators contribute to each probe.
Three Probe Types and Their Semantics
**Liveness probe** — is the container still alive? If this fails, K8s kills and restarts the container. Only fail liveness for truly unrecoverable states — infinite loop, deadlock, corrupted state. NEVER fail liveness for a slow external dependency (DB timeout, downstream service unavailable). That would cause a restart storm.
**Readiness probe** — is the container ready to serve requests? If this fails, the pod is removed from the Service endpoint list — no new requests are routed to it, but the pod keeps running. Correct for: warming up caches, DB connection not yet established, circuit breaker open.
**Startup probe** — replaces liveness during the initial startup window. Gives slow-starting JVM apps time to boot without K8s killing them as "not alive" before they are ready.
# Kubernetes Deployment — health probe configuration
spec:
containers:
- name: order-service
image: myregistry/order-service:1.2.3
# Startup probe — runs first; liveness/readiness only start after this succeeds
startupProbe:
httpGet:
path: /actuator/health/liveness
port: 8081
failureThreshold: 30 # 30 × 10s = 5 minutes for JVM to start
periodSeconds: 10
# Liveness probe — checked throughout lifetime; failure → restart
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8081
initialDelaySeconds: 0 # startup probe handles the delay
periodSeconds: 10
failureThreshold: 3
# Readiness probe — failure → removed from load balancer
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8081
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 3Spring Boot Actuator Liveness & Readiness
Spring Boot 2.3+ automatically manages liveness and readiness state. The ApplicationContext publishes AvailabilityChangeEvent events that update the internal state. You can programmatically change state — for example, mark the service as not-ready when a critical background job fails.
# application.yml — expose liveness + readiness via Actuator
management:
health:
livenessState:
enabled: true
readinessState:
enabled: true
endpoint:
health:
group:
liveness:
include: livenessState
readiness:
include: "readinessState,db,redis" # db+redis failure → not ready
// Programmatically change readiness state
@Component
@RequiredArgsConstructor
public class DatabaseWarmupRunner implements ApplicationRunner {
private final ApplicationEventPublisher events;
private final DataSource dataSource;
@Override
public void run(ApplicationArguments args) throws Exception {
try {
// Verify DB connection and warm up the connection pool
dataSource.getConnection().close();
// All good — signal ready for traffic
events.publishEvent(new AvailabilityChangeEvent<>(this, ReadinessState.ACCEPTING_TRAFFIC));
} catch (Exception e) {
log.error("DB not available — staying in REFUSING_TRAFFIC state", e);
events.publishEvent(new AvailabilityChangeEvent<>(this, ReadinessState.REFUSING_TRAFFIC));
}
}
}Graceful Shutdown — Handling In-Flight Requests
When K8s terminates a pod (SIGTERM signal), the container has terminationGracePeriodSeconds (default 30s) to finish in-flight requests before it is force-killed (SIGKILL). Spring Boot's graceful shutdown (server.shutdown=graceful) stops accepting new requests and waits for running ones to complete within the configured timeout. Set terminationGracePeriodSeconds slightly greater than the Spring Boot timeout to avoid race conditions.
# application.yml — graceful shutdown
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 25s # wait up to 25s for in-flight requests
# Kubernetes — terminationGracePeriodSeconds > Spring timeout
spec:
terminationGracePeriodSeconds: 30 # 30s > 25s Spring timeout
containers:
- name: order-service
lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 5"] # give K8s 5s to drain from LB
# Shutdown sequence:
# 1. K8s sends SIGTERM → pod.Status.Phase = Terminating
# 2. Endpoints controller removes pod from Service (eventual, ~2-5s)
# 3. preStop hook: sleep 5s (overlap window for LB draining)
# 4. Spring graceful shutdown: stop accepting new requests
# 5. Wait up to 25s for in-flight requests to complete
# 6. Spring context closes (@PreDestroy, connection pools drained)
# 7. SIGKILL if still running after terminationGracePeriodSecondsKey Points to Remember
- 1Liveness → restart pod on failure; Readiness → remove from LB endpoints on failure (pod stays running).
- 2Never fail liveness for a slow DB — that causes a restart storm. Fail readiness for temporarily degraded dependencies.
- 3Startup probe protects slow-starting JVMs: liveness and readiness only activate after the startup probe succeeds.
- 4Spring Boot 2.3+ exposes /actuator/health/liveness and /actuator/health/readiness automatically with livenessState and readinessState enabled.
- 5Programmatically publish AvailabilityChangeEvent<ReadinessState> to dynamically toggle readiness based on application logic.
- 6Set terminationGracePeriodSeconds > Spring's timeout-per-shutdown-phase and add a preStop sleep to allow the LB to drain before Spring stops accepting requests.
Interview Questions
Sign in to ask AriaWhat is the difference between a liveness probe and a readiness probe in Kubernetes?
Should a slow database connection cause a liveness probe failure? Why or why not?
What is a startup probe and when would you use it?
How does Spring Boot's graceful shutdown interact with Kubernetes pod termination?
A pod is taking 30 seconds to start. Liveness probes are failing and K8s keeps restarting it. How do you fix this?
Ask Aria about Health Checks & Readiness Probes
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.