ASP.NET Core  

Building Health Checks and Readiness Probes in ASP.NET Core 11

Modern cloud-native applications must do more than simply start successfully—they need to continuously report whether they're healthy and ready to serve requests. Containers may start before dependencies are available, databases may become unreachable, or external services may fail unexpectedly.

ASP.NET Core provides a built-in Health Checks framework that integrates with orchestrators like Kubernetes, Docker, Azure App Service, and load balancers. Properly configured health checks help automate traffic routing, restarts, and monitoring.

In this article, you'll learn how to implement production-ready health checks in ASP.NET Core 11, differentiate between liveness and readiness probes, and understand how to validate your implementation using a structured testing methodology.

Note: This article focuses on implementation patterns and testing methodology. It does not include fabricated availability or performance metrics.

Why Health Checks Matter

Without health checks, infrastructure cannot determine whether an application can safely process requests.

Typical failure scenarios include:

  • Database connection failures

  • Redis unavailable

  • RabbitMQ disconnected

  • External API outages

  • Disk space issues

  • Background worker failures

Health checks allow monitoring systems to detect these failures automatically.

Understanding Health Check Types

ProbePurposeTypical Response
LivenessIs the application running?Restart if unhealthy
ReadinessCan the application accept traffic?Remove from load balancer
StartupHas initialization completed?Delay readiness until startup finishes

Each probe serves a different purpose in cloud-native deployments.

Create the Project

dotnet new webapi -n HealthChecksDemo

Register Health Checks

Enable the Health Checks framework.

builder.Services.AddHealthChecks();

This registers the infrastructure required to expose health endpoints.

Map Health Check Endpoints

Expose a basic endpoint.

app.MapHealthChecks("/health");

Request:

GET /health

Successful response:

Healthy

Add a SQL Server Health Check

Install the package.

dotnet add package Microsoft.Extensions.Diagnostics.HealthChecks.SqlServer

Register the check.

builder.Services
    .AddHealthChecks()
    .AddSqlServer(
        builder.Configuration.GetConnectionString("Default"));

If SQL Server becomes unavailable, the health endpoint reports an unhealthy state.

Add a Redis Health Check

Install:

dotnet add package AspNetCore.HealthChecks.Redis

Configure:

builder.Services
    .AddHealthChecks()
    .AddRedis(
        "localhost:6379",
        name: "redis");

This verifies Redis connectivity.

Add a Custom Health Check

Sometimes application-specific checks are required.

Example:

using Microsoft.Extensions.Diagnostics.HealthChecks;

public class StorageHealthCheck : IHealthCheck
{
    public Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken = default)
    {
        var available = true;

        if (!available)
            return Task.FromResult(
                HealthCheckResult.Unhealthy(
                    "Storage unavailable"));

        return Task.FromResult(
            HealthCheckResult.Healthy());
    }
}

Register it.

builder.Services
    .AddHealthChecks()
    .AddCheck<StorageHealthCheck>(
        "storage");

Separate Liveness and Readiness

Register tagged checks.

builder.Services
    .AddHealthChecks()
    .AddSqlServer(
        connectionString,
        tags: new[] { "ready" });

Expose separate endpoints.

app.MapHealthChecks("/health/live");

app.MapHealthChecks("/health/ready",
    new HealthCheckOptions
    {
        Predicate = check =>
            check.Tags.Contains("ready")
    });

This separation allows infrastructure to distinguish between application availability and dependency readiness.

Return Detailed Health Information

Customize the response.

app.MapHealthChecks("/health",
    new HealthCheckOptions
    {
        ResponseWriter = async (context, report) =>
        {
            context.Response.ContentType =
                "application/json";

            await context.Response.WriteAsJsonAsync(new
            {
                Status = report.Status.ToString(),
                Results = report.Entries.Select(e => new
                {
                    Name = e.Key,
                    Status = e.Value.Status.ToString()
                })
            });
        }
    });

Example response:

{
  "status": "Healthy",
  "results": [
    {
      "name": "sql",
      "status": "Healthy"
    },
    {
      "name": "redis",
      "status": "Healthy"
    }
  ]
}

Kubernetes Configuration

Example readiness probe.

readinessProbe:
  httpGet:
    path: /health/ready
    port: 80

Example liveness probe.

livenessProbe:
  httpGet:
    path: /health/live
    port: 80

Kubernetes automatically removes unhealthy pods from service and restarts failed containers when appropriate.

End-to-End Health Check Flow

A typical workflow is:

  1. Application starts.

  2. Health checks are registered.

  3. Infrastructure calls /health/live.

  4. Readiness probe verifies dependencies.

  5. Healthy instances receive traffic.

  6. Failed checks report unhealthy status.

  7. Load balancer or orchestrator removes unhealthy instances.

  8. Containers restart if necessary.

Built-in vs Custom Checks

Health CheckUse Case
SQL ServerDatabase connectivity
RedisCache availability
RabbitMQMessaging infrastructure
URL CheckExternal APIs
Disk StorageStorage capacity
Custom CheckBusiness-specific validation

Combine built-in and custom checks for comprehensive monitoring.

Testing Methodology

Health checks validate operational readiness rather than application performance.

Test Environment

Maintain consistency for:

  • .NET SDK version

  • Infrastructure versions

  • Network configuration

  • Container runtime

  • Health check intervals

Test Scenarios

Evaluate:

  • Database unavailable

  • Redis disconnected

  • External API timeout

  • Slow dependency startup

  • Container restart

  • Multiple dependency failures

Metrics to Observe

Collect:

  • Health check response time

  • Failure detection time

  • Recovery time

  • Restart frequency

  • Readiness transitions

  • Dependency availability

Useful Tools

Useful tools include:

  • Kubernetes Dashboard

  • Azure Monitor

  • Prometheus

  • Grafana

  • Application Insights

  • dotnet-counters

  • Docker logs

Test failure scenarios in staging before deploying to production.

Best Practices

  • Separate liveness and readiness probes.

  • Keep liveness checks lightweight.

  • Include critical dependencies in readiness checks.

  • Return structured JSON responses.

  • Monitor health endpoint failures.

  • Avoid expensive database queries inside health checks.

  • Use tags to organize checks.

  • Regularly test failure and recovery scenarios.

Common Mistakes

MistakeImpact
Using one endpoint for all probesIncorrect restart behavior
Performing expensive operationsSlow health responses
Including optional services in livenessUnnecessary container restarts
Ignoring transient failuresFalse alarms
Exposing sensitive diagnostic informationSecurity risks
Never testing failure scenariosUnexpected production behavior

Troubleshooting

Health Endpoint Always Returns Unhealthy

Verify:

  • Database connectivity

  • Redis connection

  • Custom health check logic

  • Service registration

Kubernetes Restarts Containers Continuously

Review:

  • Liveness probe configuration

  • Probe intervals

  • Timeout settings

  • Startup duration

Readiness Never Becomes Healthy

Check:

  • Dependency initialization

  • Connection strings

  • Authentication

  • Network access

FAQs

What is the difference between liveness and readiness?

Liveness determines whether the application is still running. Readiness determines whether it can safely handle incoming requests.

Should every dependency be included in health checks?

Only dependencies that are essential for serving requests should typically be included in readiness checks. Optional services may require different handling.

Can I create custom health checks?

Yes. Implement the IHealthCheck interface to validate application-specific resources or business requirements.

Are health checks expensive?

They should not be. Keep them lightweight and avoid long-running operations or complex queries.

Can monitoring tools consume health endpoints?

Yes. Platforms such as Kubernetes, Azure Monitor, Prometheus, and load balancers commonly use health endpoints to monitor application status.

Conclusion

Health checks are a fundamental part of building resilient ASP.NET Core applications. By continuously verifying application status and dependency availability, they enable orchestrators and monitoring platforms to make intelligent decisions about routing traffic, restarting services, and detecting failures.

Combining built-in health checks with custom validations, separating liveness from readiness, and regularly testing failure scenarios ensures your applications remain reliable and production-ready across modern cloud environments.