Health Checks in ASP.NET Core

Health checks let your application report whether it can serve traffic. Orchestrators like Kubernetes, load balancers, and monitoring tools use these endpoints to decide whether to route requests to an instance, restart it, or raise an alert.

Basic Setup

ASP.NET Core includes health check infrastructure out of the box:

Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHealthChecks();

var app = builder.Build();

app.MapHealthChecks("/healthz");

app.Run();

A GET request to /healthz returns a 200 OK with the body Healthy. That is enough for a basic liveness check, but real applications need to verify their dependencies.

Checking Dependencies

Create a custom health check by implementing IHealthCheck:

DatabaseHealthCheck.cs
public class DatabaseHealthCheck : IHealthCheck
{
    private readonly IDbContextFactory<AppDbContext> _factory;

    public DatabaseHealthCheck(IDbContextFactory<AppDbContext> factory)
    {
        _factory = factory;
    }

    public async Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken = default)
    {
        try
        {
            using var db = await _factory.CreateDbContextAsync(cancellationToken);
            await db.Database.CanConnectAsync(cancellationToken);
            return HealthCheckResult.Healthy("Database connection is available.");
        }
        catch (Exception ex)
        {
            return HealthCheckResult.Unhealthy(
                "Database connection failed.", ex);
        }
    }
}

Register it with tags so you can group checks into different endpoints:

Program.cs
builder.Services.AddHealthChecks()
    .AddCheck<DatabaseHealthCheck>(
        "database",
        failureStatus: HealthStatus.Unhealthy,
        tags: new[] { "ready" })
    .AddCheck<RedisHealthCheck>(
        "redis",
        failureStatus: HealthStatus.Degraded,
        tags: new[] { "ready" });

Liveness vs Readiness

A common pattern in container orchestration is to separate liveness and readiness probes:

Example.cs
// Liveness — always healthy if the process is running
app.MapHealthChecks("/healthz/live", new HealthCheckOptions
{
    Predicate = _ => false // Run no checks — just return healthy
});

// Readiness — check all dependencies tagged "ready"
app.MapHealthChecks("/healthz/ready", new HealthCheckOptions
{
    Predicate = check => check.Tags.Contains("ready")
});

In a Kubernetes deployment, configure these as separate probes:

config.yaml
livenessProbe:
  httpGet:
    path: /healthz/live
    port: 8080
readinessProbe:
  httpGet:
    path: /healthz/ready
    port: 8080

Custom Response Format

By default, the health endpoint returns a plain text status. For monitoring dashboards, a JSON response with per-check details is more useful:

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

        var result = new
        {
            status = report.Status.ToString(),
            duration = report.TotalDuration.TotalMilliseconds,
            checks = report.Entries.Select(e => new
            {
                name = e.Key,
                status = e.Value.Status.ToString(),
                description = e.Value.Description,
                duration = e.Value.Duration.TotalMilliseconds,
                exception = e.Value.Exception?.Message
            })
        };

        await context.Response.WriteAsJsonAsync(result);
    }
});

Using NuGet Packages

The AspNetCore.HealthChecks.* community packages provide pre-built checks for common dependencies:

Example.cs
builder.Services.AddHealthChecks()
    .AddSqlServer(
        builder.Configuration.GetConnectionString("Default")!,
        name: "sql-server",
        tags: new[] { "ready" })
    .AddRedis(
        builder.Configuration.GetConnectionString("Redis")!,
        name: "redis",
        tags: new[] { "ready" })
    .AddUrlGroup(
        new Uri("https://api.example.com/status"),
        name: "external-api",
        tags: new[] { "ready" });

These packages cover SQL Server, PostgreSQL, Redis, RabbitMQ, Azure services, and dozens more.

Health Check UI

The AspNetCore.HealthChecks.UI package provides a dashboard that polls your health endpoints and displays their status over time. This is particularly useful during development and for internal monitoring:

Program.cs
builder.Services.AddHealthChecksUI()
    .AddInMemoryStorage();

app.MapHealthChecksUI();

Key Takeaways

Health checks are essential infrastructure for any production application. Separate liveness from readiness probes, tag your checks for flexible endpoint composition, and return structured JSON responses for monitoring tools. The community NuGet packages save considerable time for common dependency checks.