Home/Learn/Microservices/Health Checks & Readiness Probes

Health Checks & Readiness Probes

Intermediate
Observability

Liveness 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.

YAML — Kubernetes Health Probes
# 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: 3

Spring 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.

YAML + Java — Spring Boot Health State
# 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.

YAML — Graceful Shutdown
# 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 terminationGracePeriodSeconds

Key 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 Aria
1

What is the difference between a liveness probe and a readiness probe in Kubernetes?

EasyAmazon
2

Should a slow database connection cause a liveness probe failure? Why or why not?

MediumGoogle
3

What is a startup probe and when would you use it?

MediumFlipkart
4

How does Spring Boot's graceful shutdown interact with Kubernetes pod termination?

HardNetflix
5

A pod is taking 30 seconds to start. Liveness probes are failing and K8s keeps restarting it. How do you fix this?

MediumUber

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.

Loading discussion…