Part 7 – Building Self-Healing Spring Boot Applications with Kubernetes Health Probes

Why “Running” Doesn’t Mean Your Application Is Ready

“A container can be running, the JVM can be alive, and yet your application may still be incapable of serving a single customer request. Kubernetes doesn’t guess the health of your application—it asks the application itself.”


Tuesday Morning, 8:30 AM

Sarah had just completed one of the smoothest production deployments the Digital Payments team had ever experienced.

The lessons from the previous deployment had paid off.

The Payment Service now supported graceful shutdown.

Rolling deployments completed without interrupting customer transactions.

The Operations dashboard remained green throughout the deployment.

Everything looked perfect.

At 9:15 AM, however, a different alert appeared.

Payment API Error Rate: 38%

Operations immediately checked Kubernetes.

NAME                                READY   STATUS
payment-service-6dc9bfffd9-f82pk    1/1     Running
payment-service-6dc9bfffd9-hl6ts    1/1     Running
payment-service-6dc9bfffd9-vwxk9    1/1     Running

Every Pod was running.

No restarts.

No deployment failures.

No node issues.

Yet customers were receiving HTTP 500 responses.

Sarah opened the application logs.

Connecting to Oracle...

Loading Currency Cache...

Loading Holiday Calendar...

Initializing Fraud Rules...

Subscribing to Solace...

Connecting to Redis...

The application wasn’t broken.

It simply wasn’t ready.

Unfortunately, Kubernetes didn’t know that.

Traffic had already started flowing to the new Pods.


The Difference Between Running and Ready

One of the first concepts every Spring Boot developer must understand is that Kubernetes separates process health from application readiness.

Imagine opening a brand-new bank branch.

At 8:45 AM:

  • The lights are on.
  • Employees are inside.
  • Computers are booting.
  • Cash drawers are being prepared.
  • Security systems are being tested.

Technically, the building is operational.

Would you allow customers inside?

Of course not.

The branch isn’t ready yet.

Spring Boot applications behave the same way.


The Startup Timeline of a Typical Enterprise Service

Let’s examine what actually happens when our Payment Service starts.

Container Started

        │

        ▼

JVM Starts

        │

        ▼

Spring Context Created

        │

        ▼

Dependency Injection

        │

        ▼

Embedded Tomcat Starts

        │

        ▼

Oracle Connection Pool Created

        │

        ▼

Redis Connected

        │

        ▼

Kafka Consumers Registered

        │

        ▼

Solace Subscribers Created

        │

        ▼

Holiday Cache Loaded

        │

        ▼

Fraud Rules Loaded

        │

        ▼

Health Indicators Registered

        │

        ▼

Application Ready

Notice something interesting.

The container became Running near the top of the timeline.

Business readiness happened much later.

For a simple REST API, this difference might be only two or three seconds.

For an enterprise banking application, it can easily exceed one minute.


Why Kubernetes Needs Health Probes

Kubernetes cannot understand Java.

It cannot determine whether your caches are loaded.

It cannot verify whether Oracle connections have been established.

It cannot know whether Kafka consumers have subscribed successfully.

Instead, Kubernetes continuously asks your application three simple questions.

Have you started?

        │

        ▼

Are you healthy?

        │

        ▼

Can I send customer traffic to you?

Each question has a different answer.

Each answer results in a different Kubernetes action.

Understanding these three questions is essential to building self-healing systems.


The Three Health Probes

Kubernetes provides three independent health probes.

                 Spring Boot Application

                       │

        ┌──────────────┼──────────────┐

        ▼              ▼              ▼

 Startup Probe   Liveness Probe   Readiness Probe

Although they often use the same HTTP endpoint, their purpose is completely different.

Many production issues arise because teams configure all three probes identically.

Let’s examine each one carefully.


Startup Probe – “Have You Finished Starting?”

The Startup Probe exists for one reason.

It protects slow-starting applications.

Imagine our Payment Service requires sixty seconds to initialize.

During that time it performs the following tasks.

  • Build Spring Context
  • Connect Oracle
  • Connect Redis
  • Connect Kafka
  • Connect Solace
  • Load Holiday Calendar
  • Load Currency Exchange Rates
  • Warm fraud rule cache

Until this process completes, the application should not be considered unhealthy.

It is simply still starting.

Without a Startup Probe, Kubernetes may mistakenly conclude that the application has failed.

The result looks something like this.

Container Starts

↓

Application Still Starting

↓

Probe Fails

↓

Container Restarted

↓

Application Starts Again

↓

Probe Fails Again

Eventually Kubernetes reports:

CrashLoopBackOff

Ironically, nothing was wrong with the application.

It simply needed more time.

The Startup Probe prevents this unnecessary restart cycle.


Readiness Probe – “Can Customers Use You?”

This is perhaps the most important probe in enterprise applications.

Readiness has nothing to do with whether the application is alive.

Instead, it answers a business question.

“Can this instance successfully process customer requests?”

Suppose Oracle becomes unavailable.

Should Kubernetes restart the application?

Probably not.

Restarting won’t repair Oracle.

Instead, the application reports:

Readiness = DOWN

Immediately Kubernetes removes the Pod from the Service.

Before

Service

↓

Pod A

Pod B

Pod C

----------------

After

Service

↓

Pod A

Pod C

Notice something remarkable.

The Pod is still running.

The JVM continues executing.

Background processing may continue.

Only customer traffic stops.

As soon as Oracle reconnects, Readiness becomes UP again.

The Pod automatically rejoins the load balancer.

No restart required.


Liveness Probe – “Are You Still Alive?”

Liveness serves a completely different purpose.

Suppose a deadlock occurs inside the JVM.

All request threads become blocked.

CPU usage drops.

Memory usage remains normal.

The Java process still exists.

Linux believes everything is healthy.

Customers, however, experience endless timeouts.

This is exactly the type of failure Liveness is designed to detect.

When the Liveness Probe repeatedly fails, Kubernetes concludes that the application is unlikely to recover on its own.

It terminates the container and starts a fresh instance.

This is Kubernetes’ self-healing mechanism in action.


Understanding the Difference

A helpful way to think about the three probes is to compare them to an airline.

Startup

Has the aircraft completed boarding and preparation?

Readiness

Can passengers safely board?

Liveness

Is the aircraft still operational during the flight?

Each question is asked at a different point in the journey.

The answers trigger different operational decisions.


Integrating Spring Boot Actuator

Spring Boot provides first-class support for Kubernetes health probes through Spring Boot Actuator.

Instead of creating custom controllers, we expose dedicated health endpoints.

/actuator/health

/actuator/health/liveness

/actuator/health/readiness

Behind these endpoints, Spring Boot aggregates the health of multiple components.

For example:

  • Oracle Database
  • Redis
  • Kafka
  • Solace
  • Disk Space
  • Custom business health indicators

This allows Kubernetes to make informed decisions without understanding the internals of the application.


Designing Health Checks Correctly

One of the most common mistakes is checking everything in every probe.

Imagine the Fraud Service is temporarily unavailable.

Should the Payment Service fail its Liveness Probe?

No.

Restarting the Payment Service won’t repair the Fraud Service.

Instead:

  • Liveness should remain UP.
  • Readiness may become DOWN if requests cannot be processed safely.

The rule is simple.

Liveness should answer:

“Should Kubernetes restart me?”

Readiness should answer:

“Should Kubernetes send me traffic?”

Those are two completely different questions.


Enterprise Health Indicators

For our Payment Service, the health model might look like this.

Startup

  • Spring Context initialized
  • Database migrations complete
  • Holiday cache loaded
  • Fraud rules initialized
  • Messaging subscriptions established

Readiness

  • Oracle reachable
  • Redis available
  • Solace connected
  • Kafka consumers active
  • Required downstream services accessible

Liveness

  • JVM responsive
  • Application threads functioning
  • No fatal deadlocks
  • Internal event loop operational

Notice that business dependencies influence Readiness far more than Liveness.


When Things Go Wrong

Let’s consider a few real production scenarios.

Scenario 1 – Oracle Restart

Oracle undergoes scheduled maintenance.

Result:

Startup   ✓

Liveness  ✓

Readiness ✗

Traffic shifts to healthy replicas.

The application stays alive.


Scenario 2 – JVM Deadlock

A synchronization bug freezes request processing.

Startup   ✓

Readiness ✗

Liveness ✗

Kubernetes restarts the Pod.


Scenario 3 – Slow Startup

The Fraud Rule Engine now loads several million records.

Startup requires ninety seconds.

Startup ✗

Readiness Ignored

Liveness Ignored

Kubernetes patiently waits.

No unnecessary restarts occur.


Common Mistakes

Over the years, a few mistakes appear repeatedly.

Using the same endpoint for every probe

Each probe serves a different purpose.

Treat them differently.

Checking downstream systems in Liveness

This often creates endless restart loops.

Returning HTTP 200 unconditionally

If your application always reports “healthy,” Kubernetes loses its ability to recover from failures.

Ignoring startup duration

Measure startup under production conditions rather than on a developer laptop.


Health Probes and Self-Healing

Let’s step back and appreciate what we’ve built over the last three articles.

During deployment:

  • Graceful shutdown protects existing requests.
  • Startup Probe protects initialization.
  • Readiness controls traffic.
  • Liveness detects unrecoverable failures.

Together they create a continuous feedback loop.

Application Starts

↓

Startup Probe

↓

Readiness Probe

↓

Customer Traffic

↓

Failure Occurs

↓

Liveness Probe

↓

Automatic Recovery

↓

Readiness Restored

↓

Traffic Resumes

Notice that no engineer manually restarts the application.

The platform continuously observes, evaluates, and recovers.

This is what self-healing really means.

It isn’t magic.

It’s the result of applications exposing accurate health information and Kubernetes responding intelligently.


Production Checklist

Before deploying any Spring Boot microservice, verify the following:

  • Startup time has been measured.
  • Startup Probe is configured for slow initialization.
  • Readiness reflects business readiness rather than process status.
  • Liveness detects only unrecoverable application failures.
  • Spring Boot Actuator is enabled.
  • Custom health indicators exist for critical dependencies.
  • Probe timeouts match real production behavior.
  • Health endpoints are tested during deployments and infrastructure failures.

Teams that invest time in designing meaningful health checks experience significantly fewer deployment issues and faster recovery from unexpected failures.


Looking Ahead

Our Payment Service is now truly cloud-native.

It starts gracefully.

It shuts down gracefully.

It tells Kubernetes when it is ready.

It allows Kubernetes to recover automatically from failures.

But one question still remains.

What happens when customer traffic suddenly doubles during a festival sale or payroll processing day?

Should Operations manually increase the number of replicas?

Or can Kubernetes scale the application automatically based on real-time demand?

In the next article, we’ll explore Horizontal Pod Autoscaling (HPA) and learn how Kubernetes dynamically scales Spring Boot microservices using CPU, memory, and custom application metrics while balancing performance, resilience, and infrastructure cost.

Series recommendation

At this stage, we’ve built a strong “application-first” foundation:

  1. Designing Spring Boot Applications for Kubernetes
  2. Graceful Shutdown & Zero-Downtime Deployments
  3. Health Probes & Self-Healing

The next logical arc before returning to Kubernetes infrastructure would be:

  • Part 8 – Spring Boot Configuration Management (ConfigMaps, Secrets, Profiles & Feature Flags)
  • Part 9 – Resource Management (CPU, Memory, JVM, Requests, Limits & OOMKilled)
  • Part 10 – Horizontal Pod Autoscaling (HPA)

This progression mirrors the challenges enterprise teams encounter after their first successful deployment and keeps the series highly practical for Java developers.

Leave a Reply

Your email address will not be published. Required fields are marked *