Modern applications rarely run on a single server. They are deployed across containers, Kubernetes clusters, cloud platforms, load balancers, and microservice architectures. In these environments, simply knowing that an application process is running isn't enough. An application might be online but unable to connect to its database, communicate with external services, or process requests successfully.

Health checks provide a standardized way to determine whether an application is healthy, ready to receive traffic, or needs to be restarted. ASP.NET Core includes built-in health check support, allowing developers to expose endpoints that monitoring systems and orchestrators can use to make deployment and scaling decisions.

In this article, you'll learn how to implement production-ready health checks in ASP.NET Core 10, monitor dependencies, distinguish between liveness and readiness probes, and follow best practices for reliable application monitoring.

Why Health Checks Matter

Process Running Doesn't Mean Application Is Healthy

Consider an order processing application.

Client
   │
   ▼
ASP.NET Core API
   │
   ├────────► SQL Database
   │
   ├────────► Redis Cache
   │
   ├────────► Payment Gateway
   │
   └────────► Message Queue

The application process may still be running even if:

Without health checks, load balancers and orchestration platforms continue routing requests to an unhealthy instance, resulting in failed requests and poor user experience.

Understanding Health Checks

What Is a Health Check?

A health check is an endpoint that reports the current state of an application and its critical dependencies.

Typical health checks verify:

Health endpoints are intended for monitoring systems rather than end users.

Configuring Health Checks

Register health check services during application startup.

builder.Services.AddHealthChecks();

Expose the endpoint.

var app = builder.Build();

app.MapHealthChecks("/health");

Why This Configuration?

The AddHealthChecks() method registers the health check infrastructure, while MapHealthChecks() exposes an HTTP endpoint that monitoring systems can query.

This provides a centralized mechanism for reporting application health without adding custom controller logic.

Monitoring Database Connectivity

Applications often depend on a database.

builder.Services
    .AddHealthChecks()
    .AddSqlServer(
        builder.Configuration.GetConnectionString(
            "DefaultConnection")!);

Why Check the Database?

A running application cannot process business requests if it cannot communicate with its database.

Database health checks detect connectivity issues before users begin experiencing widespread failures.

Creating Custom Health Checks

Business-specific dependencies may require custom validation.

public sealed class PaymentGatewayHealthCheck
    : IHealthCheck
{
    public async Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken)
    {
        var gatewayAvailable = true;

        if (!gatewayAvailable)
        {
            return HealthCheckResult.Unhealthy(
                "Payment gateway unavailable.");
        }

        return HealthCheckResult.Healthy();
    }
}

Register the custom check.

builder.Services
    .AddHealthChecks()
    .AddCheck<PaymentGatewayHealthCheck>(
        "Payment Gateway");

Why Create Custom Checks?

Built-in health checks cover common infrastructure components, but many production systems rely on external services that require custom validation.

Custom health checks allow applications to verify dependencies that are unique to their business domain.

Liveness vs Readiness

Health checks generally fall into two categories.

Liveness Probe

Answers the question:

"Should this application be restarted?"

A liveness probe checks whether the application process is functioning correctly.

Typical checks include:

Readiness Probe

Answers the question:

"Can this application receive traffic?"

A readiness probe validates whether required dependencies are available.

Examples include:

Separating these probes prevents unnecessary application restarts while ensuring unhealthy instances stop receiving requests.

End-to-End Implementation

Consider an order processing platform deployed to Kubernetes.

Architecture:

Customer
     │
     ▼
Load Balancer
     │
     ▼
Kubernetes
     │
 ┌───┴───────────────┐
 ▼                   ▼
Health Checks     ASP.NET Core API
                        │
        ┌───────────────┴───────────────┐
        ▼               ▼               ▼
    SQL Database     Redis Cache   Payment Gateway

Workflow:

  1. Kubernetes starts a new application instance.

  2. The readiness probe verifies database and cache connectivity.

  3. Once healthy, traffic is routed to the instance.

  4. During runtime, liveness probes monitor application responsiveness.

  5. If the application becomes unresponsive, Kubernetes restarts the container.

  6. If an external dependency fails, readiness checks temporarily remove the instance from load balancing until it recovers.

This strategy improves application availability while reducing failed requests during dependency outages.

Health Status Responses

ASP.NET Core health checks typically return one of three states.

StatusMeaningTypical Action
HealthyApplication is operating normallyContinue serving traffic
DegradedApplication is functioning with limited capabilityMonitor closely
UnhealthyCritical dependency failureRemove from traffic or restart

Using these standardized states allows monitoring platforms to respond consistently across multiple applications.

Best Practices

Common Mistakes

One common mistake is performing expensive business operations inside health checks. Health endpoints should execute quickly and avoid placing additional load on production systems.

Another issue is checking only whether the application process is running. A healthy process does not guarantee that required services such as databases or caches are available.

Developers also sometimes expose detailed health information publicly. Internal dependency details should generally be restricted to trusted monitoring systems.

Testing and Validation

Before deploying health checks, validate the following:

Regular validation ensures monitoring systems respond correctly during real production incidents.

Performance Considerations

Health checks execute frequently, especially in containerized environments.

To minimize overhead:

Efficient health checks improve observability without introducing measurable application overhead.

Security Considerations

Health endpoints provide operational insights and should be secured appropriately.

Follow these recommendations:

Production health endpoints should balance operational visibility with security.

Troubleshooting

Health Endpoint Always Reports Healthy

Verify that dependency checks have been registered correctly and that custom health checks are included in the health check pipeline.

Readiness Probe Never Succeeds

Review startup configuration and ensure required services such as databases, caches, and message brokers are available before marking the application as ready.

Kubernetes Continuously Restarts the Application

Check whether liveness probes are too aggressive or whether the application requires additional startup time before health checks begin.

External Dependency Causes Frequent Failures

Consider reporting a Degraded status instead of Unhealthy for non-critical services to avoid unnecessary removal from load balancing.

Conclusion

Health checks are a fundamental part of building reliable ASP.NET Core 10 applications. By monitoring databases, caches, external services, and custom business dependencies, applications can provide accurate health information to load balancers, Kubernetes, and monitoring platforms. Separating liveness and readiness checks, keeping health probes lightweight, and integrating them into deployment workflows helps ensure applications remain resilient, scalable, and easier to operate in production.