Back to blog

Kubernetes-ready .NET health checks: live, ready, and startup done right

Liveness, readiness, and startup probes do different jobs. Here is how we map them to three tagged .NET health endpoints so orchestrators never route traffic to a half-initialized pod.

Tutorials
April 24, 2026 9 min read
Kubernetes-ready .NET health checks: live, ready, and startup done right

A single /health endpoint that returns 200 is one of the most common production traps in containerized .NET. It conflates three different questions that Kubernetes asks separately, and getting them confused leads to two classic failures: traffic routed to pods that are not ready, and healthy pods killed mid-warmup. The fix is to answer each question with its own endpoint.

Three probes, three questions

Kubernetes probes are not interchangeable:

  • Liveness - is the process wedged and in need of a restart? If this fails, the pod is killed.
  • Readiness - can the pod serve traffic right now? If this fails, it is pulled from the load balancer but not killed.
  • Startup - has the app finished its potentially slow initialization? Until this passes, liveness and readiness are held off so a slow start is not mistaken for a hang.

Mapping all three to one check guarantees you get at least one of them wrong.

Tagging checks in ASP.NET Core

The ASP.NET Core health-check system lets you tag individual checks and expose endpoints filtered by tag. Register checks with the tags that describe which probe they belong to:

CSHARP
builder.Services.AddHealthChecks()
    .AddCheck("self", () => HealthCheckResult.Healthy(), tags: ["live"])
    .AddNpgSql(connString, tags: ["ready"])
    .AddCheck<WarmupHealthCheck>("warmup", tags: ["startup"]);

Then map three endpoints, each filtering by a tag predicate:

CSHARP
app.MapHealthChecks("/alive", new() { Predicate = r => r.Tags.Contains("live") });
app.MapHealthChecks("/health", new() { Predicate = r => r.Tags.Contains("ready") });
app.MapHealthChecks("/startup", new() { Predicate = r => r.Tags.Contains("startup") });

Now /alive stays cheap and dependency-free, /health checks the things you need to serve a request, and /startup reports initialization progress.

The warmup gate

The subtle part is startup. Migrations, cache priming, and bus connections take time, and during that window the app is running but not ready. A warmup gate - a hosted service that holds /startup unhealthy until initialization completes - solves this without Thread.Sleep or guesswork:

CSHARP
public sealed class StartupWarmupGate : IHostedService
{
    private volatile bool _ready;
    public bool IsReady => _ready;
    public void MarkReady() => _ready = true;
    public Task StartAsync(CancellationToken ct) => Task.CompletedTask;
    public Task StopAsync(CancellationToken ct) => Task.CompletedTask;
}

The startup health check simply reports the gate's state, and your initialization code calls MarkReady() when migrations and warmup actually finish.

Note: Keep liveness dumb. A liveness check that touches the database will restart your pods during a database blip - turning a recoverable dependency hiccup into a cascading outage.

Wiring the probes

The deployment manifest points each probe at its endpoint, with the startup probe given a generous failure threshold so slow-but-healthy starts are tolerated:

YAML
startupProbe:
  httpGet: { path: /startup, port: 8080 }
  failureThreshold: 30
  periodSeconds: 5
livenessProbe:
  httpGet: { path: /alive, port: 8080 }
readinessProbe:
  httpGet: { path: /health, port: 8080 }

Wrapping up

Three probes exist because they answer three different questions, and your app should answer them separately. Tag your health checks, expose /alive, /health, and /startup, and gate startup behind a real warmup signal. Do that and Kubernetes will route traffic only when you are genuinely ready and restart only when you are genuinely stuck - which is the entire point of health checks.

Want your services production-hardened from day one? Talk to us.

Keep reading

The four pillars of ConnectSoft: Factory, Platforms, Squads, and Marketplace
Company News
June 20, 2026 8 min read

The four pillars of ConnectSoft: Factory, Platforms, Squads, and Marketplace

ConnectSoft is more than a services company. Here is how four reinforcing lines - the AI Factory, live platforms, AI squads, and a marketplace - fit together into one ecosystem.

Read more
The ConnectSoft tech landscape: the stack behind our delivery
Company News
June 18, 2026 8 min read

The ConnectSoft tech landscape: the stack behind our delivery

A tour of the technologies we lean on across delivery - .NET and Blazor, Azure, messaging, data, observability, and AI - and the principles that decide what makes the cut.

Read more
Why we standardized on the Microsoft Agent Framework
Engineering
June 14, 2026 9 min read

Why we standardized on the Microsoft Agent Framework

Choosing the runtime for an autonomous agent mesh is a long-lived bet. Here is the reasoning behind picking the Microsoft Agent Framework, with MCP tools and a model abstraction at its core.

Read more

Subscribe to our newsletter

Get the latest tech insights straight to your inbox

Ready to work with us?

Let's discuss how our team can help achieve your digital goals