Self-Healing Microservices: Implementing Health Checks with Spring Boot and Kubernetes
Building resilient microservices isn’t just about writing fault-tolerant code—it’s about creating systems that can detect failures and recover automatically. This is the core idea behind self-healing microservices.
In this article, you’ll learn how to:
- Implement liveness and readiness probes in Spring Boot.
- Create custom health indicators using Spring Actuator.
- Configure Kubernetes for auto-restart and traffic control.
- Debug and troubleshoot health check issues.
Let’s get started.
1. Why Self-Healing Matters
Failures are inevitable in distributed systems. Network timeouts, memory leaks, thread deadlocks, or even simple bugs can bring down a service. The goal is not to prevent all failures but to detect and recover gracefully, minimizing downtime.
Kubernetes, combined with Spring Boot’s Actuator health endpoints, provides a robust foundation for this.
2. Liveness vs Readiness Probes
| Probe | Purpose | Effect on Pod |
|---|---|---|
| Liveness | Checks if the app is alive. If not, Kubernetes restarts the pod. | Pod is restarted. |
| Readiness | Checks if the app is ready to handle traffic. If not, Kubernetes removes it from the load balancer. | Traffic stops flowing to the pod. |
Step 1: Add Spring Boot Actuator
Start by adding Spring Boot Actuator to expose health endpoints:
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Step 2: Configure Actuator Endpoints
In application.yml, expose liveness and readiness:
management:
endpoints:
web:
exposure:
include: health, info
endpoint:
health:
probes:
enabled: true
By setting probes.enabled: true, Spring Boot automatically exposes:
/actuator/health/liveness/actuator/health/readiness
Step 3: Define Kubernetes Probes
Here’s how to configure Kubernetes probes in your Deployment YAML:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-service
spec:
replicas: 3
selector:
matchLabels:
app: my-service
template:
metadata:
labels:
app: my-service
spec:
containers:
- name: my-service
image: my-service:latest
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 10
periodSeconds: 15
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
Step 4: Add Custom Health Indicators
Sometimes the default probes aren’t enough. You may need to check:
- Database connectivity
- External service availability
- Message queue readiness
Create a custom health indicator:
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
@Override
public Health health() {
boolean dbUp = checkDatabase();
if (dbUp) {
return Health.up().withDetail("database", "Available").build();
} else {
return Health.down().withDetail("database", "Unavailable").build();
}
}
private boolean checkDatabase() {
// Simulate DB check
return true; // Replace with real logic
}
}
Step 5: Auto-Restart Logic with Kubernetes
Once Kubernetes detects a liveness failure, it automatically:
- Sends a
SIGTERM - Waits for graceful shutdown (default 30s)
- Kills the container and restarts it
This reduces the need for manual intervention.
Common failure scenarios handled automatically:
| Failure | Handled by |
|---|---|
| Deadlocks | Liveness probe restart |
| Out-of-memory crash | K8s OOM killer + restart |
| Downstream service crash | Readiness probe pause traffic |
3. Debugging and Troubleshooting
- Check logs
kubectl logs my-service-pod
2. Describe pod for probe errors
kubectl describe pod my-service-pod
Look for:
Last State: TerminatedReason: Probe failedEvents:sections
- Manually test probes
curl http://localhost:8080/actuator/health/liveness curl http://localhost:8080/actuator/health/readiness
Step 6: Use Spring Boot Graceful Shutdown
To avoid abrupt shutdowns during restarts, enable graceful shutdown:
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
Override DisposableBean or @PreDestroy to clean up resources.
4. Useful Links & Resources
- Spring Boot Actuator Docs
https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html - Kubernetes Probes Docs
https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ - Graceful Shutdown in Spring Boot
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.graceful-shutdown - Kubernetes Health Checks Best Practices
https://kubernetes.io/blog/2018/01/18/debugging-application-failures-in-kubernetes/ - Example GitHub Repo (Spring Boot + K8s Health Checks)
https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator
5. Final Thoughts
Self-healing microservices are critical for resilient architectures. By combining Spring Boot Actuator health checks with Kubernetes liveness/readiness probes, you can:
- Minimize downtime
- Detect failures early
- Let Kubernetes handle automatic recovery
This approach leads to systems that heal themselves, requiring less human intervention and ensuring higher availability.




