Implement Health Checks in C# ASP.NET Core
This guide demonstrates how to implement health checks in C# using ASP.NET Core to monitor application availability, uptime, and system dependencies. You will learn how to enable built-in diagnostic endpoints, create custom checks for external services like databases or third-party APIs, separate readiness from liveness probes, and format the output for automated monitoring systems.
1. Setting Up Basic Health Checks
ASP.NET Core includes native middleware for health monitoring. To get
started, register the health check services in the dependency injection
container and map the endpoint in your Program.cs file.
var builder = WebApplication.CreateBuilder(args);
// Register health check services
builder.Services.AddHealthChecks();
var app = builder.Build();
// Map the health check route
app.MapHealthChecks("/health");
app.Run();Running the application and navigating to /health
returns a plain-text response of Healthy with an HTTP
status code 200 OK. If a check fails, it returns
Unhealthy with an HTTP status code
503 Service Unavailable.
2. Creating Custom Health Checks
To monitor application dependencies such as databases, caches, or
remote HTTP services, implement the IHealthCheck
interface.
using Microsoft.Extensions.Diagnostics.HealthChecks;
public class DatabaseHealthCheck : IHealthCheck
{
public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
try
{
// Simulate a database ping or connection test
bool isDatabaseConnected = await CheckDatabaseConnectionAsync(cancellationToken);
if (isDatabaseConnected)
{
return HealthCheckResult.Healthy("Database connection is operational.");
}
return HealthCheckResult.Degraded("Database response latency is higher than expected.");
}
catch (Exception ex)
{
return HealthCheckResult.Unhealthy("Database connection failed.", ex);
}
}
private static Task<bool> CheckDatabaseConnectionAsync(CancellationToken cancellationToken)
{
// Replace with actual database ping logic
return Task.FromResult(true);
}
}Register the custom health check in Program.cs:
builder.Services.AddHealthChecks()
.AddCheck<DatabaseHealthCheck>("database_check", tags: new[] { "ready", "db" });3. Configuring Liveness and Readiness Probes
Container orchestrators like Kubernetes distinguish between whether an application process is alive (Liveness) and whether it is ready to accept incoming traffic (Readiness). You can implement this separation using tags.
// Register checks with specific tags
builder.Services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy(), tags: new[] { "live" })
.AddCheck<DatabaseHealthCheck>("database", tags: new[] { "ready" });
var app = builder.Build();
// Liveness probe: returns 200 if the process is alive
app.MapHealthChecks("/health/live", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
{
Predicate = check => check.Tags.Contains("live")
});
// Readiness probe: returns 200 only if all required dependencies are healthy
app.MapHealthChecks("/health/ready", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready")
});4. Formatting Health Check Responses as JSON
By default, ASP.NET Core returns a plain-text status string. To integrate with logging pipelines and monitoring dashboards, customize the response to output structured JSON:
using System.Text.Json;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
app.MapHealthChecks("/health/details", new HealthCheckOptions
{
ResponseWriter = async (context, report) =>
{
context.Response.ContentType = "application/json";
var response = new
{
Status = report.Status.ToString(),
TotalDuration = report.TotalDuration.TotalMilliseconds,
Checks = report.Entries.Select(entry => new
{
Component = entry.Key,
Status = entry.Value.Status.ToString(),
Description = entry.Value.Description,
Duration = entry.Value.Duration.TotalMilliseconds,
Error = entry.Value.Exception?.Message
})
};
await context.Response.WriteAsync(JsonSerializer.Serialize(response, new JsonSerializerOptions
{
WriteIndented = true
}));
}
});This endpoint returns a comprehensive snapshot of all registered checks, enabling automated platforms to detect partial degradations or specific component failures instantly.