Skip to content

codingdroplets/dotnet-health-checks-api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dotnet-health-checks-api

A complete guide to implementing health checks in ASP.NET Core — covering liveness probes, readiness probes, custom IHealthCheck implementations, and a rich JSON detail endpoint.

Visit CodingDroplets YouTube Patreon Buy Me a Coffee GitHub


🚀 Support the Channel — Join on Patreon

If this sample saved you time, consider joining our Patreon community. You'll get exclusive .NET tutorials, premium code samples, and early access to new content — all for the price of a coffee.

👉 Join CodingDroplets on Patreon

Prefer a one-time tip? Buy us a coffee ☕


🎯 What You'll Learn

  • How to implement custom IHealthCheck classes for database, external service, and memory checks
  • The difference between liveness and readiness probes — and why they matter in Kubernetes and cloud deployments
  • How to register and tag health checks to control which probes run which checks
  • How to build a rich JSON /healthz/detail endpoint with structured diagnostic data
  • How to configure custom HTTP status codes for each health state (Healthy, Degraded, Unhealthy)
  • How to write integration tests for health endpoints using WebApplicationFactory<T>

🗺️ Architecture Overview

HTTP Request
     │
     ▼
┌─────────────────────────────────────────────────────┐
│                  ASP.NET Core Pipeline              │
│                                                     │
│  ┌──────────────────────────────────────────────┐  │
│  │           Health Check Endpoints             │  │
│  │                                              │  │
│  │  GET /healthz         → All checks (plain)  │  │
│  │  GET /healthz/live    → "live" tag only      │  │
│  │  GET /healthz/ready   → "ready" tag only     │  │
│  │  GET /healthz/detail  → All checks (JSON)   │  │
│  └──────────────────────────────────────────────┘  │
│                        │                            │
│         ┌──────────────┼──────────────┐             │
│         ▼              ▼              ▼             │
│  ┌─────────────┐ ┌──────────┐ ┌───────────────┐   │
│  │  Database   │ │ External │ │    Memory     │   │
│  │HealthCheck  │ │ Service  │ │  HealthCheck  │   │
│  │             │ │HealthChk │ │               │   │
│  │ tag: ready  │ │tag:ready │ │  tag: live    │   │
│  └─────────────┘ └──────────┘ └───────────────┘   │
└─────────────────────────────────────────────────────┘

Result: HealthCheckResponse (JSON)
  ├── status: "Healthy" | "Degraded" | "Unhealthy"
  ├── checkedAt: UTC timestamp
  ├── totalDurationMs: number
  └── checks[]: name, status, description, durationMs, tags, data

📋 Summary Table

Check Interface Tags Failure Status Purpose
DatabaseHealthCheck IHealthCheck ready, database Unhealthy Verifies DB connectivity
ExternalServiceHealthCheck IHealthCheck ready, external Degraded Pings downstream HTTP service
MemoryHealthCheck IHealthCheck live, memory Degraded Monitors GC memory allocation
Endpoint Checks Run Response Format Use Case
GET /healthz All Plain text Simple status
GET /healthz/live live tagged Plain text Kubernetes liveness probe
GET /healthz/ready ready tagged Plain text Kubernetes readiness probe
GET /healthz/detail All Rich JSON Dashboards, alerting, debugging

📁 Project Structure

dotnet-health-checks-api/
├── dotnet-health-checks-api.sln
│
├── HealthChecksApi/                        ← Main Web API project
│   ├── HealthChecks/
│   │   ├── DatabaseHealthCheck.cs          ← IHealthCheck: DB connectivity
│   │   ├── ExternalServiceHealthCheck.cs   ← IHealthCheck: downstream HTTP service
│   │   └── MemoryHealthCheck.cs            ← IHealthCheck: process memory usage
│   ├── Models/
│   │   └── HealthCheckResponse.cs          ← Response models for /healthz/detail
│   ├── Properties/
│   │   └── launchSettings.json
│   ├── Program.cs                          ← Service registration + endpoint mapping
│   ├── appsettings.json
│   └── appsettings.Development.json
│
└── HealthChecksApi.Tests/                  ← xUnit test project
    ├── HealthChecks/
    │   ├── DatabaseHealthCheckTests.cs     ← Unit tests for DatabaseHealthCheck
    │   └── MemoryHealthCheckTests.cs       ← Unit tests for MemoryHealthCheck
    └── Integration/
        └── HealthEndpointTests.cs          ← Integration tests for all endpoints

🛠️ Prerequisites

Requirement Version
.NET SDK 8.0 or later (sample uses 10.0)
IDE Visual Studio 2022+ / VS Code / Rider
Database Not required — DB check is simulated (swap in real connection for production)

⚡ Quick Start

# 1. Clone the repository
git clone https://github.com/codingdroplets/dotnet-health-checks-api.git
cd dotnet-health-checks-api

# 2. Build the solution
dotnet build -c Release

# 3. Run the API
cd HealthChecksApi
dotnet run

# 4. Test the health endpoints
curl http://localhost:5289/healthz
curl http://localhost:5289/healthz/live
curl http://localhost:5289/healthz/ready
curl http://localhost:5289/healthz/detail

Visual Studio users: press F5 — the browser will open Swagger UI automatically.


🔧 How It Works

1. Implement IHealthCheck

Each health check implements the IHealthCheck interface with a single method:

public class DatabaseHealthCheck : IHealthCheck
{
    public async Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken = default)
    {
        // Check database connectivity
        // Return Healthy, Degraded, or Unhealthy
        return HealthCheckResult.Healthy("Database is reachable.", data: new Dictionary<string, object>
        {
            { "latencyMs", 5 }
        });
    }
}

2. Register Health Checks with Tags

Tags let you group checks for liveness vs. readiness probes:

builder.Services.AddHealthChecks()
    .AddCheck<DatabaseHealthCheck>(
        name: "database",
        failureStatus: HealthStatus.Unhealthy,
        tags: ["ready", "database"])
    .AddCheck<MemoryHealthCheck>(
        name: "memory",
        failureStatus: HealthStatus.Degraded,
        tags: ["live", "memory"]);

3. Map Endpoints with Predicates

Filter which checks run on each endpoint using tag predicates:

// Liveness probe — only "live" checks
app.MapHealthChecks("/healthz/live", new HealthCheckOptions
{
    Predicate = check => check.Tags.Contains("live"),
    ResultStatusCodes =
    {
        [HealthStatus.Unhealthy] = StatusCodes.Status503ServiceUnavailable
    }
});

// Readiness probe — only "ready" checks
app.MapHealthChecks("/healthz/ready", new HealthCheckOptions
{
    Predicate = check => check.Tags.Contains("ready"),
    ResultStatusCodes =
    {
        [HealthStatus.Unhealthy] = StatusCodes.Status503ServiceUnavailable
    }
});

4. Build a Rich JSON Response

Use a custom ResponseWriter to return structured JSON:

app.MapHealthChecks("/healthz/detail", new HealthCheckOptions
{
    ResponseWriter = async (context, report) =>
    {
        context.Response.ContentType = "application/json";
        var response = new HealthCheckResponse
        {
            Status = report.Status.ToString(),
            CheckedAt = DateTime.UtcNow,
            Checks = report.Entries.Select(e => new HealthCheckEntry
            {
                Name = e.Key,
                Status = e.Value.Status.ToString(),
                Description = e.Value.Description,
                DurationMs = e.Value.Duration.TotalMilliseconds,
                Tags = e.Value.Tags,
                Data = e.Value.Data.Count > 0 ? e.Value.Data : null
            })
        };
        await context.Response.WriteAsync(JsonSerializer.Serialize(response, ...));
    }
});

📡 Health Check Endpoints

Method Endpoint Description Healthy Status Unhealthy Status
GET /healthz Overall health (all checks, plain text) 200 OK 503 Service Unavailable
GET /healthz/live Liveness probe (live tag only) 200 OK 503 Service Unavailable
GET /healthz/ready Readiness probe (ready tag only) 200 OK 503 Service Unavailable
GET /healthz/detail Rich JSON with all check details 200 OK 503 Service Unavailable

Sample /healthz/detail Response

{
  "status": "Healthy",
  "checkedAt": "2026-03-28T06:00:00Z",
  "totalDurationMs": 18.42,
  "checks": [
    {
      "name": "database",
      "status": "Healthy",
      "description": "Database is reachable and responding.",
      "durationMs": 6.1,
      "tags": ["ready", "database"],
      "data": {
        "check": "database",
        "status": "healthy",
        "latencyMs": 5
      }
    },
    {
      "name": "memory",
      "status": "Healthy",
      "description": "Memory usage is normal: 32.14 MB.",
      "durationMs": 0.3,
      "tags": ["live", "memory"],
      "data": {
        "check": "memory",
        "allocatedMB": 32.14
      }
    }
  ]
}

🧪 Running Tests

# Run all tests
dotnet test -c Release

# Run with verbose output
dotnet test -c Release --logger "console;verbosity=detailed"

Test Summary

Test Class Type Tests Covers
DatabaseHealthCheckTests Unit 3 DB check: healthy, degraded (no conn string), data keys
MemoryHealthCheckTests Unit 3 Memory check: normal usage, thresholds in data, check key
HealthEndpointTests Integration 7 All endpoints: status codes, JSON format, field presence
Total 13

🤔 Key Concepts

Liveness vs. Readiness

Concept Liveness Probe Readiness Probe
Question "Is the process alive?" "Is it ready to serve traffic?"
Failure action Container is restarted Pod is removed from load balancer
Checks included Lightweight (memory, CPU) Dependency checks (DB, APIs)
Kubernetes config livenessProbe readinessProbe
Endpoint /healthz/live /healthz/ready

Health Status Values

Status Meaning HTTP Code
Healthy Everything is fine 200 OK
Degraded Working but below optimal 200 OK
Unhealthy Critical failure 503 Service Unavailable

Why Not Just Return 200 Always?

Returning 200 when the database is down hides the failure from Kubernetes, load balancers, and monitoring tools. Proper health checks enable:

  • Self-healing — Kubernetes restarts unhealthy pods automatically
  • Zero-downtime deployments — readiness probes prevent traffic before the app is ready
  • Observability — monitoring tools can alert on degraded states before they become failures

🏷️ Technologies Used

  • ASP.NET Core 10 — Web API framework
  • .NET 10 — Runtime (compatible with .NET 8+)
  • Microsoft.Extensions.Diagnostics.HealthChecks — Built-in health check framework
  • xUnit — Test framework
  • Microsoft.AspNetCore.Mvc.Testing — Integration testing with WebApplicationFactory<T>

📚 References


📄 License

This project is licensed under the MIT License.


🔗 Connect with CodingDroplets

Platform Link
🌐 Website https://codingdroplets.com/
📺 YouTube https://www.youtube.com/@CodingDroplets
🎁 Patreon https://www.patreon.com/CodingDroplets
☕ Buy Me a Coffee https://buymeacoffee.com/codingdroplets
💻 GitHub http://github.com/codingdroplets/

Want more samples like this? Support us on Patreon or buy us a coffee ☕ — every bit helps keep the content coming!

About

Health checks in ASP.NET Core: custom IHealthCheck implementations for database, external service, and memory — with liveness, readiness, and rich JSON diagnostic endpoints.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages