/actuator/health endpoint used in earlier versions. To ensure reliable container orchestration in Docker and Kubernetes, you must configure separate endpoints for startup completion and service availability. This prevents premature traffic routing to containers that are initialized but not yet ready to handle requests.
Understanding Liveness vs Readiness Probes
In modern microservice architectures, distinguishing between a process being alive and a service being ready is essential for stability. The liveness probe checks if the application process is running and responsive. If this check fails, Kubernetes assumes the container is in a bad state and restarts it immediately. The readiness probe, however, verifies if the application can accept traffic. This includes checking database connections, cache availability, or external API dependencies. Previously, Spring Boot exposed a single health status at/actuator/health. This forced operators to choose between restarting unhealthy apps or blocking traffic to uninitialized ones. By splitting these concerns, you gain granular control over deployment lifecycle management. The liveness endpoint should only monitor the JVM itself, while the readiness endpoint monitors all dependent services.
Configuring Spring Boot Actuators
To implement this separation, you need to enable the specific actuator endpoints in yourapplication.properties or application.yml. Ensure that the management endpoints are exposed via HTTP so that the orchestrator can reach them.
management.endpoints.web.exposure.include=health,liveness,readiness
Next, define the mapping paths for these endpoints. While Spring Boot provides default mappings, explicitly setting them ensures clarity across different environments. The liveness probe typically maps to /actuator/health/liveness, and the readiness probe maps to /actuator/health/readiness.
management.endpoint.health.liveness-state.enabled=true
management.endpoint.health.readiness-state.enabled=true
You can also customize the response codes. By default, both return HTTP 200 for success. You might want to return 503 Service Unavailable if the application is not ready, which signals Kubernetes to stop sending traffic until the next successful check.
Docker Container Health Checks
While Kubernetes handles the orchestration logic, Docker also supports health checks during local development or standalone deployments. You can define a health check in yourDockerfile using the HEALTHCHECK instruction. This command runs periodically inside the container to verify its operational status.
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD curl -f http://localhost:8080/actuator/health/liveness || exit 1
The --start-period flag is crucial for Spring Boot applications. It gives the JVM time to initialize beans and connect to databases before the first health check triggers. Without this delay, the container might be marked as unhealthy before the application has fully started, leading to unnecessary restarts.
For production, rely on Kubernetes probes rather than Docker health checks, as K8s offers more sophisticated retry policies and integration with pod lifecycle events. However, keeping the Docker health check consistent with the liveness probe ensures parity between local and remote environments.
Image Pull Backoff Error: Could Not Pull Image from Docker Registry often occurs when base images are misconfigured, but proper health checks help diagnose runtime issues after the image is successfully loaded.
Kubernetes Probe Configuration
In your Kubernetes Deployment manifest, define both liveness and readiness probes for each container. These probes tell the kubelet how to interact with your application.spec:
containers:
- name: my-service
image: my-service:latest
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
The initialDelaySeconds for liveness should be higher than readiness because the application needs more time to become fully operational. The readiness probe can start checking sooner, as it only needs to verify basic connectivity.
If the liveness probe fails, Kubernetes kills the pod and starts a new one. If the readiness probe fails, Kubernetes removes the pod from the service endpoint list, stopping traffic flow but keeping the pod running. This allows developers to fix transient issues without losing state or triggering full restarts unnecessarily.
Troubleshooting Java Connect Exception: Connection Refused in Docker Compose Spring Boot Keycloak highlights common dependency failures that readiness probes are designed to catch gracefully.
Custom Health Indicators
Spring Boot allows you to create custom health indicators for specific components. Implement theHealthIndicator interface to add checks for Redis, RabbitMQ, or custom APIs. These indicators contribute to the overall readiness status.
@Component
public class CustomServiceHealthIndicator implements HealthIndicator {
@Override
public Health health() {
try {
// Check connection to custom service
if (isConnected()) {
return Health.up().withDetail("status", "connected").build();
} else {
return Health.down().withDetail("error", "connection failed").build();
}
} catch (Exception e) {
return Health.down(e).build();
}
}
}
By aggregating these checks, the readiness probe provides a comprehensive view of system health. This ensures that traffic is only routed to instances where all critical dependencies are functioning correctly.
Frequently Asked Questions
What happens if the liveness probe fails?
Kubernetes will immediately terminate the container and schedule a new one. This is intended for unrecoverable states where the application is stuck or deadlocked.
Can I use the same endpoint for both probes?
No, using the same endpoint defeats the purpose of separation. A single endpoint cannot distinguish between a JVM crash and a temporary dependency failure.
How do I test health endpoints locally?
Run your Spring Boot application and navigate to http://localhost:8080/actuator/health/liveness in your browser or use curl to verify the JSON response.
What if my database is down?
The liveness probe should still pass because the JVM is running. The readiness probe should fail, preventing traffic from reaching the pod until the database is restored.
Is it safe to remove the readiness probe?
Removing it risks routing traffic to pods that are initializing or have failed dependencies, leading to increased error rates and poor user experience.