Have you ever gotten a bug report stating that an API is responding slowly, but you are unable to identify the cause of the delay?
Before providing a response, the request may take some time to run application code, wait for a database query, call another API, or process data. You cannot determine which component is at fault by looking simply at the overall response time.
In a microservices application, where a single request may go through many services before receiving the final result, this becomes much more challenging.
This is a common problem in production environments. Checking whether an application process is running does not tell you whether the application can actually serve requests.
This is where health checks become useful.
Health checks allow you to monitor whether an application and its important dependencies are working as expected. You can use them to check database connectivity, verify access to required services, and provide health information to monitoring systems or container orchestration platforms.
In this article, we will learn how to add health checks to an ASP.NET Core application, check SQL Server connectivity, expose health endpoints, and configure different health checks for production use.
What Are Health Checks in ASP.NET Core?
Health checks are endpoints or checks that report the condition of an application or one of its dependencies.
ASP.NET Core includes built-in health-check services through Microsoft.Extensions.Diagnostics.HealthChecks. You can register checks using dependency injection and expose their results through HTTP endpoints.
For example, an application might need the following components to work correctly:
- The ASP.NET Core application itself.
- A SQL Server database.
- A Redis cache.
- An external service used to process payments.
A health check can help determine whether these components are available.
However, health checks do not automatically detect every application problem. You must decide which components to check and what each result should mean for your application.
Liveness vs. Readiness Health Checks
Before writing code, it is important to understand the difference between liveness and readiness.
Liveness answers the question: Is the application process healthy enough to continue running?
Readiness answers a different question: Is the application ready to receive requests?
Consider an application that has started but is still loading required configuration or cannot access a critical dependency. It might be alive but not ready to serve traffic.
| Feature | Liveness Check | Readiness Check |
|---|---|---|
| Purpose | Determines whether the application should continue running | Determines whether the application should receive traffic |
| Typical checks | Basic process or application responsiveness | Required dependencies and initialization state |
| Common use | Container restart decisions | Load balancer traffic decisions |
| Database required? | Usually not | Depends on application requirements |
Keeping these checks separate helps prevent unnecessary restarts. For example, a temporary database outage should not automatically cause an orchestrator to restart an otherwise healthy application repeatedly.
Step 1: Register Health Checks
Create an ASP.NET Core Web API project if you do not already have one.
1 2 | dotnet new webapi -n HealthCheckDemo cd HealthCheckDemo |
Open Program.cs and register the health-check services.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); builder.Services.AddHealthChecks(); var app = builder.Build(); app.UseHttpsRedirection(); app.MapControllers(); app.MapHealthChecks("/health"); app.Run(); |
The important line is:
1 | builder.Services.AddHealthChecks(); |
It registers the health-check services with dependency injection.
The following line exposes the health-check endpoint:
1 | app.MapHealthChecks("/health"); |
After starting the application, send a GET request to /health.
If no additional checks have been registered, the endpoint can report that the application is healthy because there are no failing checks.
This is a useful starting point, but it does not verify whether SQL Server or another required dependency is available.
Step 2: Add a SQL Server Health Check
Many applications depend on a database. If the database becomes unavailable, operations that require it may fail even though the application process continues running.
ASP.NET Core supports database health checks through provider-specific health-check packages.
For SQL Server, install the SQL Server health-check package:
1 | dotnet add package AspNetCore.HealthChecks.SqlServer |
Next, configure the connection string in appsettings.json.
1 2 3 4 5 | { "ConnectionStrings": { "DefaultConnection": "Server=YOUR_SERVER;Database=YOUR_DATABASE;User Id=YOUR_USER;Password=YOUR_PASSWORD;Encrypt=True;TrustServerCertificate=False;" } } |
Replace the placeholder values with your actual database configuration. Do not commit real production credentials to source control.
Now register the SQL Server health check.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException( "The database connection string is missing."); builder.Services .AddHealthChecks() .AddSqlServer( connectionString, name: "sqlserver", tags: new[] { "ready" }); var app = builder.Build(); app.UseHttpsRedirection(); app.MapControllers(); app.MapHealthChecks("/health"); app.Run(); |
The AddSqlServer() method registers a check that verifies database connectivity.
The name identifies the check, while the ready tag lets you select it for a readiness endpoint later.
The check’s exact behavior depends on the package version and configuration. Make sure the package is compatible with your target framework.
Step 3: Create Separate Liveness and Readiness Endpoints
A single health endpoint may be enough for a small application, but production systems often benefit from separate liveness and readiness endpoints.
For example, liveness should generally avoid checking remote dependencies. Otherwise, a temporary database outage could make the application appear unhealthy enough to restart.
Readiness, on the other hand, can include required dependencies that must be available before the application receives traffic.
Update Program.cs as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.Extensions.Diagnostics.HealthChecks; var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException( "The database connection string is missing."); builder.Services .AddHealthChecks() .AddSqlServer( connectionString, name: "sqlserver", tags: new[] { "ready" }); var app = builder.Build(); app.UseHttpsRedirection(); app.MapControllers(); app.MapHealthChecks("/health/live", new HealthCheckOptions { Predicate = _ => false }); app.MapHealthChecks("/health/ready", new HealthCheckOptions { Predicate = check => check.Tags.Contains("ready") }); app.Run(); |
Let’s understand the configuration.
The liveness endpoint uses:
1 | Predicate = _ => false |
This excludes all registered health checks. It provides a basic endpoint indicating that the application can respond to the health-check request.
The readiness endpoint uses:
1 | Predicate = check => check.Tags.Contains("ready") |
This runs only the health checks tagged with ready, including the SQL Server check.
Your monitoring system can now use the endpoints for different purposes:
| Endpoint | Purpose |
|---|---|
/health/live | Check whether the application responds |
/health/ready | Check whether the application and required dependencies are ready |
For more complex applications, liveness may need an application-specific check that detects a genuinely unrecoverable internal state. Avoid adding routine dependency checks to liveness without a clear reason.
Step 4: Return Useful Health-Check Results
By default, the health-check middleware returns a status such as Healthy, Degraded, or Unhealthy. Its default response body is simple and does not include detailed information about each check.
You can customize the response format when a monitoring system needs structured data.
For example, add a JSON response writer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | using System.Text.Json; using Microsoft.AspNetCore.Diagnostics.HealthChecks; app.MapHealthChecks("/health/ready", new HealthCheckOptions { Predicate = check => check.Tags.Contains("ready"), ResponseWriter = async (context, report) => { context.Response.ContentType = "application/json"; var result = new { status = report.Status.ToString(), checks = report.Entries.Select(entry => new { name = entry.Key, status = entry.Value.Status.ToString() }) }; await context.Response.WriteAsync( JsonSerializer.Serialize(result)); } }); |
This example returns a JSON object containing the overall status and the status of each selected check.
Use the customized endpoint instead of mapping the same readiness route twice. In a complete application, replace the earlier /health/ready mapping with this version.
For production applications, avoid returning exception messages, connection strings, server names, or other sensitive details in publicly accessible health responses. Keep detailed diagnostics in secured logs and monitoring systems.
Best Practices for Production Applications
1. Keep liveness and readiness separate
Use liveness to determine whether the application can continue running. Use readiness to determine whether it should receive traffic.
This distinction helps prevent dependency failures from triggering unnecessary restarts.
2. Set appropriate timeouts
Health checks should finish within a reasonable amount of time. A database that takes several seconds to respond should not cause health endpoints to hang indefinitely.
Configure suitable timeouts and cancellation behavior for checks that may access remote services.
3. Monitor only important dependencies
Do not add every external service to every health check. Check dependencies that matter to the endpoint’s purpose.
For example, if an optional reporting service is unavailable but the main API can still serve requests, the entire application may not need to be marked unready.
4. Secure detailed health information
Health endpoints can reveal information about internal dependencies and application structure.
Keep detailed responses private or restrict access where appropriate. Public endpoints should return only the minimum information needed by the monitoring system.
5. Use health checks with monitoring tools
Health endpoints are useful when integrated with load balancers, container orchestrators, and monitoring platforms.
However, an endpoint alone does not provide historical metrics, alerting, or trend analysis. Use a monitoring system when you need to track availability over time.
Common Mistakes and Troubleshooting
| Problem | Possible cause | What to check |
|---|---|---|
| Health endpoint returns 404 | Endpoint is not mapped or the route is incorrect | Verify MapHealthChecks() and the requested path |
| Database check fails | Invalid credentials, network issues, or unavailable SQL Server | Test connectivity and verify the connection string |
| Liveness fails during a database outage | Dependency checks are included in liveness | Separate liveness from readiness |
| Health endpoint takes too long | A dependency check is slow | Configure appropriate timeouts |
| Monitoring reports unhealthy status | A required dependency is failing | Inspect secured logs and check-level results |
| Sensitive details appear in responses | Detailed diagnostics are exposed | Restrict access and simplify the response writer |
One common mistake is treating a successful liveness check as proof that the entire application is working. It only tells you what that endpoint has been designed to verify.
Similarly, a failed readiness check does not always mean the application needs to restart. It may simply need to stop receiving traffic until a required dependency recovers.
Advantages and Disadvantages of Health Checks
Advantages
- Help detect unavailable dependencies.
- Allow load balancers to avoid sending traffic to unready instances.
- Support container orchestration and automated recovery.
- Make application availability easier to monitor.
- Help distinguish process health from dependency readiness.
Disadvantages
- Require careful configuration to avoid misleading results.
- Can add load to databases and external services if checks run too frequently.
- May cause unnecessary restarts if liveness checks depend on external services.
- Need access controls when detailed diagnostics are exposed.
Summary
Health checks are an important part of building and operating reliable ASP.NET Core applications. They help you determine whether an application is responding and whether its required dependencies are available.
With AddHealthChecks() and MapHealthChecks(), you can create health endpoints with minimal configuration. Provider-specific checks let you verify dependencies such as SQL Server, while tags allow you to separate liveness and readiness checks.
For production applications, keep these endpoints focused, configure suitable timeouts, protect sensitive diagnostic information, and integrate them with your monitoring infrastructure.
The goal is not simply to report whether the application is running. It is to provide accurate health information that helps your infrastructure respond appropriately when something goes wrong.
Recommendation for ASP.NET 11.0 Hosting
A solid base for developing online services and applications is ASP.NET Core 11. Before creating an ASP.NET web application, you must be proficient in JavaScript, HTML, CSS, and C#. There are thousands of web hosting providers offering ASP.NET hosting on the market. However, there are relatively few web hosting providers that offer top-notch ASP.NET hosting.
ASP.NET is the best development language in Windows platform, which is released by Microsoft and widely used to build all types of dynamic Web sites and XML Web services. With this article, we’re going to help you to find the best ASP.NET Hosting solution in Europe based on reliability, features, price, performance and technical support. After we reviewed about 30+ ASP.NET hosting providers in Europe, our Best ASP.NET Hosting Award in Europe goes to HostForLIFE.eu, one of the fastest growing private companies and one of the most reliable hosting providers in Europe.
