.claude/skills/health-check-endpoint/SKILL.md
Use when deploying an ASP.NET Core application to Azure App Service, Kubernetes, or any cloud platform that requires health probes, or when load balancers need to route around unhealthy instances and deployment slots must verify readiness before traffic swap. Covers AddHealthChecks registration, liveness vs readiness probe separation with tag-based filtering, custom IHealthCheck implementations, structured JSON response format, and platform-specific configuration for Azure App Service, Kubernetes, and Docker Compose. Domain: Cloud Readiness, Observability. Level: Foundational. Tags: health-check, cloud-native, monitoring, azure, kubernetes, observability.
npx skillsauth add klod68/littlerae health-check-endpointInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Cloud platforms (Azure App Service, Kubernetes, AWS ELB) need to know if your application is alive and ready to serve traffic. Without a health endpoint:
/health Endpoint with ASP.NET Core Health Checks// In Program.cs — Service registration
builder.Services.AddHealthChecks();
// In Program.cs — Endpoint mapping
app.MapHealthChecks("/health").AllowAnonymous();
This returns:
200 OK with body Healthy when the app is running503 Service Unavailable when any check fails.AllowAnonymous()Health endpoints are probed by infrastructure (load balancers, orchestrators) that doesn't authenticate. Without AllowAnonymous(), probes get 401 and the app is marked unhealthy.
builder.Services.AddHealthChecks();
app.MapHealthChecks("/health").AllowAnonymous();
builder.Services.AddHealthChecks()
.AddSqlServer(connectionString, name: "database", tags: ["ready"])
.AddRedis(redisConnectionString, name: "cache", tags: ["ready"])
.AddUrlGroup(new Uri("https://api.external.com/ping"), name: "external-api", tags: ["ready"]);
// Liveness: is the process alive? (no dependency checks)
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
Predicate = _ => false // No checks — just confirms the process is running
}).AllowAnonymous();
// Readiness: can the app serve requests? (checks dependencies)
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready")
}).AllowAnonymous();
public class DiskSpaceHealthCheck : IHealthCheck
{
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken ct = default)
{
var drive = new DriveInfo(Path.GetPathRoot(Environment.CurrentDirectory)!);
var freeGb = drive.AvailableFreeSpace / (1024.0 * 1024 * 1024);
return Task.FromResult(freeGb switch
{
< 1 => HealthCheckResult.Unhealthy($"Low disk space: {freeGb:F1} GB"),
< 5 => HealthCheckResult.Degraded($"Disk space warning: {freeGb:F1} GB"),
_ => HealthCheckResult.Healthy($"Disk space OK: {freeGb:F1} GB")
});
}
}
// Register
builder.Services.AddHealthChecks()
.AddCheck<DiskSpaceHealthCheck>("disk-space", tags: ["ready"]);
app.MapHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = async (context, report) =>
{
context.Response.ContentType = "application/json";
var result = new
{
status = report.Status.ToString(),
duration = report.TotalDuration,
checks = report.Entries.Select(e => new
{
name = e.Key,
status = e.Value.Status.ToString(),
duration = e.Value.Duration,
description = e.Value.Description,
exception = e.Value.Exception?.Message
})
};
await context.Response.WriteAsJsonAsync(result);
}
}).AllowAnonymous();
Response:
{
"status": "Healthy",
"duration": "00:00:00.0234567",
"checks": [
{ "name": "database", "status": "Healthy", "duration": "00:00:00.0120000" },
{ "name": "cache", "status": "Healthy", "duration": "00:00:00.0050000" }
]
}
In Azure Portal → App Service → Health check:
/healthlivenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 10
periodSeconds: 15
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
<!-- Core (always needed) -->
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.0" />
<!-- Database checks (optional) -->
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="8.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.Redis" Version="8.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="8.0.0" />
Note:
AddHealthChecks()is included in the ASP.NET Core shared framework — no extra package needed for basic use.
IHostedService health reporting instead)timeout in AddCheck options.Healthy/Unhealthy. JSON responses with exception messages should only be exposed on internal endpoints.HealthCheckRegistration.Delay and Period to avoid running on every probe./health in your integration test suite (see Integration Testing with WebApplicationFactory skill).tools
Use when cross-cutting concerns (logging, metrics, validation, authorization) are tangled into command handlers or service methods, when building database command pipelines with reorderable concerns, or when HTTP client pipelines or message handlers need composable, independently-replaceable processing stages. Covers ICommandInterceptor interface, InterceptorPipeline with reverse-chain construction, zero-cost Empty sentinel to skip overhead when no interceptors are registered, and ConfigureAwait(false) discipline for library code. Domain: Architecture, Cross-Cutting Concerns. Level: Intermediate. Tags: interceptor, pipeline, middleware, decorator, cross-cutting-concerns.
development
Use when writing integration tests for Razor Pages, MVC, or Minimal API applications to validate routing, middleware, page rendering, and HTTP behavior without a browser or live server, or when adding fast smoke tests to a CI pipeline. Covers WebApplicationFactory<Program> setup with public partial class Program, in-memory test server, AngleSharp HTML parsing, CSS selector assertions, redirect and status code testing, and a shared static fixture pattern for minimal per-test startup overhead. Domain: Testing, ASP.NET Core. Level: Intermediate. Tags: integration-testing, webapplicationfactory, razor-pages, anglesharp, http-testing.
development
Use when designing indexes for new tables, diagnosing slow queries that are not using indexes efficiently, reviewing index fragmentation and maintenance, or when the current indexing strategy results in key lookups, table scans, or missing index warnings. Covers clustered index key selection (narrow, unique, ever-increasing), non-clustered index design for query patterns, covering indexes with INCLUDE columns, filtered indexes for subset queries, composite index column ordering, DMV-based monitoring for missing and unused indexes, and rebuild vs reorganize maintenance thresholds. Domain: Database, Performance. Level: Intermediate. Tags: index, sql-server, covering-index, filtered-index, performance, dmv, maintenance.
development
Use when building a searchable in-memory catalog or registry for documentation sites, admin panels, or type/API browsers where you need keyword matching, fuzzy search, and ranked results without an external search engine or database. Covers RegistryService with weighted scoring across name, description, keywords, and method names; Levenshtein fuzzy matching; synonym expansion; category and subcategory filtering; and singleton DI registration for datasets of hundreds to low thousands of items. Domain: Search, Data Access Patterns. Level: Intermediate. Tags: search, registry, fuzzy-matching, in-memory, catalog, filtering.