ASP.NET Core  

Health Checks in ASP.NET Core: A Complete Guide for Reliable Applications

In modern web applications, ensuring that your system is running correctly is crucial. A sudden service failure or database outage can impact users, lead to downtime, or cause data loss. Health checks in ASP.NET Core provide a simple, structured way to monitor the health of your application and its dependencies like databases, APIs, or external services.

This article explains how to implement health checks in ASP.NET Core, step by step, with practical examples and best practices for full-stack applications.

What You Will Learn

  • What health checks are and why they are important

  • How to implement built-in health checks in ASP.NET Core

  • How to monitor dependencies like SQL Server, Redis, or APIs

  • How to create custom health checks

  • How to expose a health check endpoint

  • Integration with monitoring tools

Why Health Checks Are Important

  1. Detect failures early: Automatically detect when a service or database is down.

  2. Improve reliability: Prevent user-facing downtime by monitoring dependencies.

  3. Support DevOps practices: Integrate with Kubernetes, Docker, or load balancers.

  4. Enable proactive maintenance: Send alerts to developers or administrators when services are unhealthy.

In production, health check endpoints are often polled by monitoring tools like Prometheus, Grafana, or Azure Monitor.

Part 1: Adding Health Checks in ASP.NET Core

Step 1: Install Required Packages

ASP.NET Core has built-in support for health checks. You may install additional packages for database or external service checks:

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

Step 2: Register Health Checks

In Program.cs (ASP.NET Core 6/7+):

using Microsoft.AspNetCore.Diagnostics.HealthChecks;

var builder = WebApplication.CreateBuilder(args);

// Register health checks
builder.Services.AddHealthChecks()
    .AddSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"), 
        name: "SQL Server", 
        failureStatus: Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus.Unhealthy)
    .AddCheck("Custom", () =>
    {
        bool isHealthy = true; // Replace with custom logic
        if (isHealthy)
            return Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult.Healthy("Everything is fine");
        else
            return Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult.Unhealthy("Something is wrong");
    });

var app = builder.Build();

Step 3: Configure Health Check Endpoint

app.MapHealthChecks("/health", new HealthCheckOptions
{
    Predicate = _ => true, // Include all health checks
    ResponseWriter = async (context, report) =>
    {
        context.Response.ContentType = "application/json";

        var result = new
        {
            status = report.Status.ToString(),
            checks = report.Entries.Select(e => new {
                name = e.Key,
                status = e.Value.Status.ToString(),
                description = e.Value.Description
            })
        };

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

Now, when you visit https://localhost:5001/health, you get a JSON response like:

{
  "status": "Healthy",
  "checks": [
    {
      "name": "SQL Server",
      "status": "Healthy",
      "description": "Healthy"
    },
    {
      "name": "Custom",
      "status": "Healthy",
      "description": "Everything is fine"
    }
  ]
}

Part 2: Adding Database Health Checks

If your application depends on SQL Server:

builder.Services.AddHealthChecks()
    .AddSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"),
        name: "SQL Server", 
        failureStatus: Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus.Unhealthy);

For PostgreSQL, MySQL, Redis, or MongoDB, you can install respective NuGet packages and use a similar pattern.

Part 3: Creating Custom Health Checks

Sometimes you need to monitor external APIs, disk space, or business rules.

Create a class implementing IHealthCheck:

using Microsoft.Extensions.Diagnostics.HealthChecks;

public class ExternalApiHealthCheck : IHealthCheck
{
    private readonly HttpClient _httpClient;

    public ExternalApiHealthCheck(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        var response = await _httpClient.GetAsync("https://api.example.com/health");

        if (response.IsSuccessStatusCode)
            return HealthCheckResult.Healthy("API is available");
        else
            return HealthCheckResult.Unhealthy("API is down");
    }
}

Register the custom health check:

builder.Services.AddHttpClient();
builder.Services.AddHealthChecks()
    .AddCheck<ExternalApiHealthCheck>("External API");

Part 4: Integrating Health Checks With Angular

Angular can use HTTP requests to check application health, useful for admin dashboards.

import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-health',
  template: `
    <h2>Application Health</h2>
    <pre>{{ healthStatus | json }}</pre>
  `
})
export class HealthComponent implements OnInit {
  healthStatus: any;

  constructor(private http: HttpClient) {}

  ngOnInit() {
    this.http.get('https://localhost:5001/health').subscribe({
      next: data => this.healthStatus = data,
      error: err => this.healthStatus = { status: 'Unhealthy', error: err }
    });
  }
}

This component fetches the health endpoint and displays a structured report in JSON format.

Part 5: Best Practices

  1. Separate critical and non-critical checks

    • Database failures should mark the service unhealthy

    • Optional services (e.g., cache) can be warning only

  2. Use short response times
    Health checks should not block the application. Keep them lightweight.

  3. Integrate with monitoring systems
    Use Prometheus, Grafana, or Azure Monitor to poll health endpoints.

  4. Secure sensitive endpoints
    Consider restricting /health to internal networks or authenticated users.

  5. Use tags
    Tag health checks to differentiate between liveness and readiness checks:

.AddSqlServer(..., tags: new[] { "ready" })

Then filter in the endpoint:

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

Part 6: Liveness VS Readiness

  • Liveness check: Determines if the application is running (basic check).

  • Readiness check: Determines if the application is ready to serve traffic (database and service dependencies).

Separate endpoints

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

Conclusion

Health checks in ASP.NET Core provide a simple and powerful way to monitor application health. With health checks, you can:

  • Detect failures early

  • Improve system reliability

  • Integrate with DevOps pipelines and monitoring tools

  • Build robust full-stack applications with Angular dashboards

By combining built-in checks, database checks, and custom checks, you can ensure your application is always monitored and reliable in production.