Application freezing? How to configure liveness and readiness probes
Learn how to configure container health probes to prevent unnecessary restarts and traffic drops in your application.
Summary
- Health checks prevent orchestrators from sending traffic to unstable container instances
- Tuning intervals and failure thresholds prevents cascading restart loops
- Liveness handles internal deadlocks while readiness manages active network traffic flow
- Monitoring SLOs helps calibrate acceptable response times for automated probes
- Tools like Prometheus complement native probes to improve overall observability
Why does your application keep restarting out of nowhere?
When running applications in container orchestration systems, the orchestrator needs to know if the software is genuinely alive or just consuming memory space. In practice, this means a process might be running while remaining completely unable to respond to incoming requests due to an internal database deadlock. To prevent users from facing endless error screens, we use inspection mechanisms known as health checks. Before deploying any system to production, ensuring resilience requires understanding exactly how the orchestrator interacts with your application through automated probes.
Without proper configuration, any temporary network blip or load spike can cause the system to aggressively kill the container. This triggers a vicious cycle of unnecessary restarts that consumes precious CPU and memory resources. Furthermore, when new containers start up, they often receive live traffic before their caches are even populated, resulting in visible latency. To mitigate these scenarios, reliability engineering relies on two fundamental concepts that work together with entirely different purposes: liveness probes and readiness probes.
Understanding the difference between liveness and readiness
The liveness probe has a very specific mission: figuring out if the container has locked up completely and needs to be restarted from scratch. In practice, if this check fails repeatedly, the orchestrator destroys the current process and spins up a brand new container to take its place. A classic mistake is configuring this check to hit a heavy route that depends on external services, such as third-party APIs. If the external API drops, your entire application starts restarting unnecessarily, worsening the original issue that was entirely outside your control.
On the other hand, the readiness probe decides whether the container is ready to receive network traffic from users. Think of this as a cashier turning on an 'open' sign only after starting the terminal and counting the change in the drawer. If readiness fails, the container is not restarted; it is simply temporarily isolated from the load balancer until it successfully responds again. This separation ensures that real user requests never hit an instance that just booted up and is still warming up connections or compiling internal templates.
How to configure liveness and readiness in practice
Configuring these checks requires careful attention to waiting times and failure thresholds. If you define an interval that is too short between checks, any momentary CPU spike will be interpreted as a catastrophic failure. In practice, your container configuration file must balance the time the application takes to boot up with the frequency of routine inspections. Below is a practical example of a YAML manifest applying these guidelines in a microservices environment:
apiVersion: v1
kind: Pod
metadata:
name: my-application
spec:
containers:
- name: app
image: my-image:v1
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5In the example above, the liveness probe waits thirty seconds before testing the health route, giving the binary enough time to load into memory. Meanwhile, the readiness probe starts checking the readiness route at just five seconds, allowing traffic to flow as soon as essential components are active. This fine-tuning prevents premature restarts and ensures a smooth transition during version updates. If you need to scale complex infrastructure, it is worth checking out complementary strategies like those discussed in how to scale GitLab runners in Kubernetes to maintain operational stability.
One of the most common mistakes made by engineering teams is using the same code route for both verification types. When you point both liveness and readiness to a single endpoint that validates the database, the system becomes vulnerable to cascading systemic failures. If the database experiences a momentary slowdown, all application instances fail their liveness checks simultaneously, causing the orchestrator to kill them all at once. This creates a total blackout instead of a graceful degradation, overloading the infrastructure even further with simultaneous reconnection storms.
To prevent this unwanted behavior, create isolated, lightweight routes in your codebase for each check type. The liveness route should only check if the main thread is responding and ensure there are no severe internal memory deadlocks. Conversely, the readiness route can perform deeper validations, such as checking if core database tables respond or if local caches are populated. Furthermore, to maintain a clear view of system health in production, you can correlate these metrics with resource usage, ensuring that your pipeline and environment remain optimized as covered in our guide on how to speed up Docker builds with cache.
Final considerations
Mastering the configuration of liveness and readiness probes is the turning point between a fragile application that crashes at the first sign of stress and a resilient system that heals itself. By separating liveness logic from readiness logic, you protect your containers against aggressive restarts and prevent users from hitting unready instances. Remember to adjust timeout parameters according to your software's actual needs, avoiding excessive testing that consumes resources without bringing real value. With these practices in place, your architecture gains the stability required to operate smoothly at scale without last-minute surprises.