Health checks in ASP.NET Core
By Glenn Condron and Juergen Gutsch
Note
This isn't the latest version of this article. For the current release, see the .NET 8 version of this article.
Warning
This version of ASP.NET Core is no longer supported. For more information, see .NET and .NET Core Support Policy. For the current release, see the .NET 8 version of this article.
Important
This information relates to a pre-release product that may be substantially modified before it's commercially released. Microsoft makes no warranties, express or implied, with respect to the information provided here.
For the current release, see the .NET 8 version of this article.
ASP.NET Core offers Health Checks Middleware and libraries for reporting the health of app infrastructure components.
Health checks are exposed by an app as HTTP endpoints. Health check endpoints can be configured for various real-time monitoring scenarios:
- Health probes can be used by container orchestrators and load balancers to check an app's status. For example, a container orchestrator may respond to a failing health check by halting a rolling deployment or restarting a container. A load balancer might react to an unhealthy app by routing traffic away from the failing instance to a healthy instance.
- Use of memory, disk, and other physical server resources can be monitored for healthy status.
- Health checks can test an app's dependencies, such as databases and external service endpoints, to confirm availability and normal functioning.
Health checks are typically used with an external monitoring service or container orchestrator to check the status of an app. Before adding health checks to an app, decide on which monitoring system to use. The monitoring system dictates what types of health checks to create and how to configure their endpoints.
Basic health probe
For many apps, a basic health probe configuration that reports the app's availability to process requests (liveness) is sufficient to discover the status of the app.
The basic configuration registers health check services and calls the Health Checks Middleware to respond at a URL endpoint with a health response. By default, no specific health checks are registered to test any particular dependency or subsystem. The app is considered healthy if it can respond at the health endpoint URL. The default response writer writes HealthStatus as a plaintext response to the client. The HealthStatus
is HealthStatus.Healthy, HealthStatus.Degraded, or HealthStatus.Unhealthy.
Register health check services with AddHealthChecks in Program.cs
. Create a health check endpoint by calling MapHealthChecks.
The following example creates a health check endpoint at /healthz
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks();
var app = builder.Build();
app.MapHealthChecks("/healthz");
app.Run();
Docker HEALTHCHECK
Docker offers a built-in HEALTHCHECK
directive that can be used to check the status of an app that uses the basic health check configuration:
HEALTHCHECK CMD curl --fail http://localhost:5000/healthz || exit
The preceding example uses curl
to make an HTTP request to the health check endpoint at /healthz
. curl
isn't included in the .NET Linux container images, but it can be added by installing the required package in the Dockerfile. Containers that use images based on Alpine Linux can use the included wget
in place of curl
.
Create health checks
Health checks are created by implementing the IHealthCheck interface. The CheckHealthAsync method returns a HealthCheckResult that indicates the health as Healthy
, Degraded
, or Unhealthy
. The result is written as a plaintext response with a configurable status code. Configuration is described in the Health check options section. HealthCheckResult can also return optional key-value pairs.
The following example demonstrates the layout of a health check:
public class SampleHealthCheck : IHealthCheck
{
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context, CancellationToken cancellationToken = default)
{
var isHealthy = true;
// ...
if (isHealthy)
{
return Task.FromResult(
HealthCheckResult.Healthy("A healthy result."));
}
return Task.FromResult(
new HealthCheckResult(
context.Registration.FailureStatus, "An unhealthy result."));
}
}
The health check's logic is placed in the CheckHealthAsync method. The preceding example sets a dummy variable, isHealthy
, to true
. If the value of isHealthy
is set to false
, the HealthCheckRegistration.FailureStatus status is returned.
If CheckHealthAsync throws an exception during the check, a new HealthReportEntry is returned with its HealthReportEntry.Status set to the FailureStatus. This status is defined by AddCheck (see the Register health check services section) and includes the inner exception that caused the check failure. The Description is set to the exception's message.
Register health check services
To register a health check service, call AddCheck in Program.cs
:
builder.Services.AddHealthChecks()
.AddCheck<SampleHealthCheck>("Sample");
The AddCheck overload shown in the following example sets the failure status (HealthStatus) to report when the health check reports a failure. If the failure status is set to null
(default), HealthStatus.Unhealthy is reported. This overload is a useful scenario for library authors, where the failure status indicated by the library is enforced by the app when a health check failure occurs if the health check implementation honors the setting.
Tags can be used to filter health checks. Tags are described in the Filter health checks section.
builder.Services.AddHealthChecks()
.AddCheck<SampleHealthCheck>(
"Sample",
failureStatus: HealthStatus.Degraded,
tags: new[] { "sample" });
AddCheck can also execute a lambda function. In the following example, the health check always returns a healthy result:
builder.Services.AddHealthChecks()
.AddCheck("Sample", () => HealthCheckResult.Healthy("A healthy result."));
Call AddTypeActivatedCheck to pass arguments to a health check implementation. In the following example, a type-activated health check accepts an integer and a string in its constructor:
public class SampleHealthCheckWithArgs : IHealthCheck
{
private readonly int _arg1;
private readonly string _arg2;
public SampleHealthCheckWithArgs(int arg1, string arg2)
=> (_arg1, _arg2) = (arg1, arg2);
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context, CancellationToken cancellationToken = default)
{
// ...
return Task.FromResult(HealthCheckResult.Healthy("A healthy result."));
}
}
To register the preceding health check, call AddTypeActivatedCheck
with the integer and string passed as arguments:
builder.Services.AddHealthChecks()
.AddTypeActivatedCheck<SampleHealthCheckWithArgs>(
"Sample",
failureStatus: HealthStatus.Degraded,
tags: new[] { "sample" },
args: new object[] { 1, "Arg" });
Use Health Checks Routing
In Program.cs
, call MapHealthChecks
on the endpoint builder with the endpoint URL or relative path:
app.MapHealthChecks("/healthz");
Require host
Call RequireHost
to specify one or more permitted hosts for the health check endpoint. Hosts should be Unicode rather than punycode and may include a port. If a collection isn't supplied, any host is accepted:
app.MapHealthChecks("/healthz")
.RequireHost("www.contoso.com:5001");
To restrict the health check endpoint to respond only on a specific port, specify a port in the call to RequireHost
. This approach is typically used in a container environment to expose a port for monitoring services:
app.MapHealthChecks("/healthz")
.RequireHost("*:5001");
Warning
API that relies on the Host header, such as HttpRequest.Host and RequireHost, are subject to potential spoofing by clients.
To prevent host and port spoofing, use one of the following approaches:
- Use HttpContext.Connection (ConnectionInfo.LocalPort) where the ports are checked.
- Employ Host filtering.
To prevent unauthorized clients from spoofing the port, call RequireAuthorization:
app.MapHealthChecks("/healthz")
.RequireHost("*:5001")
.RequireAuthorization();
For more information, see Host matching in routes with RequireHost.
Require authorization
Call RequireAuthorization to run Authorization Middleware on the health check request endpoint. A RequireAuthorization
overload accepts one or more authorization policies. If a policy isn't provided, the default authorization policy is used:
app.MapHealthChecks("/healthz")
.RequireAuthorization();
Enable Cross-Origin Requests (CORS)
Although running health checks manually from a browser isn't a common scenario, CORS Middleware can be enabled by calling RequireCors
on the health checks endpoints. The RequireCors
overload accepts a CORS policy builder delegate (CorsPolicyBuilder
) or a policy name. For more information, see Enable Cross-Origin Requests (CORS) in ASP.NET Core.
Health check options
HealthCheckOptions provide an opportunity to customize health check behavior:
Filter health checks
By default, the Health Checks Middleware runs all registered health checks. To run a subset of health checks, provide a function that returns a boolean to the Predicate option.
The following example filters the health checks so that only those tagged with sample
run:
app.MapHealthChecks("/healthz", new HealthCheckOptions
{
Predicate = healthCheck => healthCheck.Tags.Contains("sample")
});
Customize the HTTP status code
Use ResultStatusCodes to customize the mapping of health status to HTTP status codes. The following StatusCodes assignments are the default values used by the middleware. Change the status code values to meet your requirements:
app.MapHealthChecks("/healthz", new HealthCheckOptions
{
ResultStatusCodes =
{
[HealthStatus.Healthy] = StatusCodes.Status200OK,
[HealthStatus.Degraded] = StatusCodes.Status200OK,
[HealthStatus.Unhealthy] = StatusCodes.Status503ServiceUnavailable
}
});
Suppress cache headers
AllowCachingResponses controls whether the Health Checks Middleware adds HTTP headers to a probe response to prevent response caching. If the value is false
(default), the middleware sets or overrides the Cache-Control
, Expires
, and Pragma
headers to prevent response caching. If the value is true
, the middleware doesn't modify the cache headers of the response:
app.MapHealthChecks("/healthz", new HealthCheckOptions
{
AllowCachingResponses = true
});
Customize output
To customize the output of a health checks report, set the HealthCheckOptions.ResponseWriter property to a delegate that writes the response:
app.MapHealthChecks("/healthz", new HealthCheckOptions
{
ResponseWriter = WriteResponse
});
The default delegate writes a minimal plaintext response with the string value of HealthReport.Status
. The following custom delegate outputs a custom JSON response using System.Text.Json:
private static Task WriteResponse(HttpContext context, HealthReport healthReport)
{
context.Response.ContentType = "application/json; charset=utf-8";
var options = new JsonWriterOptions { Indented = true };
using var memoryStream = new MemoryStream();
using (var jsonWriter = new Utf8JsonWriter(memoryStream, options))
{
jsonWriter.WriteStartObject();
jsonWriter.WriteString("status", healthReport.Status.ToString());
jsonWriter.WriteStartObject("results");
foreach (var healthReportEntry in healthReport.Entries)
{
jsonWriter.WriteStartObject(healthReportEntry.Key);
jsonWriter.WriteString("status",
healthReportEntry.Value.Status.ToString());
jsonWriter.WriteString("description",
healthReportEntry.Value.Description);
jsonWriter.WriteStartObject("data");
foreach (var item in healthReportEntry.Value.Data)
{
jsonWriter.WritePropertyName(item.Key);
JsonSerializer.Serialize(jsonWriter, item.Value,
item.Value?.GetType() ?? typeof(object));
}
jsonWriter.WriteEndObject();
jsonWriter.WriteEndObject();
}
jsonWriter.WriteEndObject();
jsonWriter.WriteEndObject();
}
return context.Response.WriteAsync(
Encoding.UTF8.GetString(memoryStream.ToArray()));
}
The health checks API doesn't provide built-in support for complex JSON return formats because the format is specific to your choice of monitoring system. Customize the response in the preceding examples as needed. For more information on JSON serialization with System.Text.Json
, see How to serialize and deserialize JSON in .NET.
Database probe
A health check can specify a database query to run as a boolean test to indicate if the database is responding normally.
AspNetCore.Diagnostics.HealthChecks
, a health check library for ASP.NET Core apps, includes a health check that runs against a SQL Server database. AspNetCore.Diagnostics.HealthChecks
executes a SELECT 1
query against the database to confirm the connection to the database is healthy.
Warning
When checking a database connection with a query, choose a query that returns quickly. The query approach runs the risk of overloading the database and degrading its performance. In most cases, running a test query isn't necessary. Merely making a successful connection to the database is sufficient. If you find it necessary to run a query, choose a simple SELECT query, such as SELECT 1
.
To use this SQL Server health check, include a package reference to the AspNetCore.HealthChecks.SqlServer
NuGet package. The following example registers the SQL Server health check:
var conStr = builder.Configuration.GetConnectionString("DefaultConnection");
if (string.IsNullOrEmpty(conStr))
{
throw new InvalidOperationException(
"Could not find a connection string named 'DefaultConnection'.");
}
builder.Services.AddHealthChecks()
.AddSqlServer(conStr);
Note
AspNetCore.Diagnostics.HealthChecks
isn't maintained or supported by Microsoft.
Entity Framework Core DbContext probe
The DbContext
check confirms that the app can communicate with the database configured for an EF Core DbContext
. The DbContext
check is supported in apps that:
- Use Entity Framework (EF) Core.
- Include a package reference to the
Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore
NuGet package.
AddDbContextCheck registers a health check for a DbContext. The DbContext
is supplied to the method as the TContext
. An overload is available to configure the failure status, tags, and a custom test query.
By default:
- The
DbContextHealthCheck
calls EF Core'sCanConnectAsync
method. You can customize what operation is run when checking health usingAddDbContextCheck
method overloads. - The name of the health check is the name of the
TContext
type.
The following example registers a DbContext
and an associated DbContextHealthCheck
:
builder.Services.AddDbContext<SampleDbContext>(options =>
options.UseSqlServer(
builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddHealthChecks()
.AddDbContextCheck<SampleDbContext>();
Separate readiness and liveness probes
In some hosting scenarios, a pair of health checks is used to distinguish two app states:
- Readiness indicates if the app is running normally but isn't ready to receive requests.
- Liveness indicates if an app has crashed and must be restarted.
Consider the following example: An app must download a large configuration file before it's ready to process requests. We don't want the app to be restarted if the initial download fails because the app can retry downloading the file several times. We use a liveness probe to describe the liveness of the process, no other checks are run. We also want to prevent requests from being sent to the app before the configuration file download has succeeded. We use a readiness probe to indicate a "not ready" state until the download succeeds and the app is ready to receive requests.
The following background task simulates a startup process that takes roughly 15 seconds. Once it completes, the task sets the StartupHealthCheck.StartupCompleted
property to true:
public class StartupBackgroundService : BackgroundService
{
private readonly StartupHealthCheck _healthCheck;
public StartupBackgroundService(StartupHealthCheck healthCheck)
=> _healthCheck = healthCheck;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// Simulate the effect of a long-running task.
await Task.Delay(TimeSpan.FromSeconds(15), stoppingToken);
_healthCheck.StartupCompleted = true;
}
}
The StartupHealthCheck
reports the completion of the long-running startup task and exposes the StartupCompleted
property that gets set by the background service:
public class StartupHealthCheck : IHealthCheck
{
private volatile bool _isReady;
public bool StartupCompleted
{
get => _isReady;
set => _isReady = value;
}
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context, CancellationToken cancellationToken = default)
{
if (StartupCompleted)
{
return Task.FromResult(HealthCheckResult.Healthy("The startup task has completed."));
}
return Task.FromResult(HealthCheckResult.Unhealthy("That startup task is still running."));
}
}
The health check is registered with AddCheck in Program.cs
along with the hosted service. Because the hosted service must set the property on the health check, the health check is also registered in the service container as a singleton:
builder.Services.AddHostedService<StartupBackgroundService>();
builder.Services.AddSingleton<StartupHealthCheck>();
builder.Services.AddHealthChecks()
.AddCheck<StartupHealthCheck>(
"Startup",
tags: new[] { "ready" });
To create two different health check endpoints, call MapHealthChecks
twice:
app.MapHealthChecks("/healthz/ready", new HealthCheckOptions
{
Predicate = healthCheck => healthCheck.Tags.Contains("ready")
});
app.MapHealthChecks("/healthz/live", new HealthCheckOptions
{
Predicate = _ => false
});
The preceding example creates the following health check endpoints:
/healthz/ready
for the readiness check. The readiness check filters health checks to those tagged withready
./healthz/live
for the liveness check. The liveness check filters out all health checks by returningfalse
in the HealthCheckOptions.Predicate delegate. For more information on filtering health checks, see Filter health checks in this article.
Before the startup task completes, the /healthz/ready
endpoint reports an Unhealthy
status. Once the startup task completes, this endpoint reports a Healthy
status. The /healthz/live
endpoint excludes all checks and reports a Healthy
status for all calls.
Kubernetes example
Using separate readiness and liveness checks is useful in an environment such as Kubernetes. In Kubernetes, an app might be required to run time-consuming startup work before accepting requests, such as a test of the underlying database availability. Using separate checks allows the orchestrator to distinguish whether the app is functioning but not yet ready or if the app has failed to start. For more information on readiness and liveness probes in Kubernetes, see Configure Liveness and Readiness Probes in the Kubernetes documentation.
The following example demonstrates a Kubernetes readiness probe configuration:
spec:
template:
spec:
readinessProbe:
# an http probe
httpGet:
path: /healthz/ready
port: 80
# length of time to wait for a pod to initialize
# after pod startup, before applying health checking
initialDelaySeconds: 30
timeoutSeconds: 1
ports:
- containerPort: 80
Distribute a health check library
To distribute a health check as a library:
Write a health check that implements the IHealthCheck interface as a standalone class. The class can rely on dependency injection (DI), type activation, and named options to access configuration data.
Write an extension method with parameters that the consuming app calls in its
Program.cs
method. Consider the following example health check, which acceptsarg1
andarg2
as constructor parameters:public SampleHealthCheckWithArgs(int arg1, string arg2) => (_arg1, _arg2) = (arg1, arg2);
The preceding signature indicates that the health check requires custom data to process the health check probe logic. The data is provided to the delegate used to create the health check instance when the health check is registered with an extension method. In the following example, the caller specifies:
arg1
: An integer data point for the health check.arg2
: A string argument for the health check.name
: An optional health check name. Ifnull
, a default value is used.failureStatus
: An optional HealthStatus, which is reported for a failure status. Ifnull
, HealthStatus.Unhealthy is used.tags
: An optionalIEnumerable<string>
collection of tags.
public static class SampleHealthCheckBuilderExtensions { private const string DefaultName = "Sample"; public static IHealthChecksBuilder AddSampleHealthCheck( this IHealthChecksBuilder healthChecksBuilder, int arg1, string arg2, string? name = null, HealthStatus? failureStatus = null, IEnumerable<string>? tags = default) { return healthChecksBuilder.Add( new HealthCheckRegistration( name ?? DefaultName, _ => new SampleHealthCheckWithArgs(arg1, arg2), failureStatus, tags)); } }
Health Check Publisher
When an IHealthCheckPublisher is added to the service container, the health check system periodically executes your health checks and calls PublishAsync with the result. This process is useful in a push-based health monitoring system scenario that expects each process to call the monitoring system periodically to determine health.
HealthCheckPublisherOptions allow you to set the:
- Delay: The initial delay applied after the app starts before executing IHealthCheckPublisher instances. The delay is applied once at startup and doesn't apply to later iterations. The default value is five seconds.
- Period: The period of IHealthCheckPublisher execution. The default value is 30 seconds.
- Predicate: If Predicate is
null
(default), the health check publisher service runs all registered health checks. To run a subset of health checks, provide a function that filters the set of checks. The predicate is evaluated each period. - Timeout: The timeout for executing the health checks for all IHealthCheckPublisher instances. Use InfiniteTimeSpan to execute without a timeout. The default value is 30 seconds.
The following example demonstrates the layout of a health publisher:
public class SampleHealthCheckPublisher : IHealthCheckPublisher
{
public Task PublishAsync(HealthReport report, CancellationToken cancellationToken)
{
if (report.Status == HealthStatus.Healthy)
{
// ...
}
else
{
// ...
}
return Task.CompletedTask;
}
}
The HealthCheckPublisherOptions class provides properties for configuring the behavior of the health check publisher.
The following example registers a health check publisher as a singleton and configures HealthCheckPublisherOptions:
builder.Services.Configure<HealthCheckPublisherOptions>(options =>
{
options.Delay = TimeSpan.FromSeconds(2);
options.Predicate = healthCheck => healthCheck.Tags.Contains("sample");
});
builder.Services.AddSingleton<IHealthCheckPublisher, SampleHealthCheckPublisher>();
AspNetCore.Diagnostics.HealthChecks
:
- Includes publishers for several systems, including Application Insights.
- Is not maintained or supported by Microsoft.
Individual Healthchecks
Delay
and Period
can be set on each HealthCheckRegistration individually. This is useful when you want to run some health checks at a different rate than the period set in HealthCheckPublisherOptions.
The following code sets the Delay
and Period
for the SampleHealthCheck1
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks()
.Add(new HealthCheckRegistration(
name: "SampleHealthCheck1",
instance: new SampleHealthCheck(),
failureStatus: null,
tags: null,
timeout: default)
{
Delay = TimeSpan.FromSeconds(40),
Period = TimeSpan.FromSeconds(30)
});
var app = builder.Build();
app.MapHealthChecks("/healthz");
app.Run();
Dependency Injection and Health Checks
It's possible to use dependency injection to consume an instance of a specific Type
inside a Health Check class. Dependency injection can be useful to inject options or a global configuration to a Health Check. Using dependency injection is not a common scenario to configure Health Checks. Usually, each Health Check is quite specific to the actual test and is configured using IHealthChecksBuilder
extension methods.
The following example shows a sample Health Check that retrieves a configuration object via dependency injection:
public class SampleHealthCheckWithDI : IHealthCheck
{
private readonly SampleHealthCheckWithDiConfig _config;
public SampleHealthCheckWithDI(SampleHealthCheckWithDiConfig config)
=> _config = config;
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context, CancellationToken cancellationToken = default)
{
var isHealthy = true;
// use _config ...
if (isHealthy)
{
return Task.FromResult(
HealthCheckResult.Healthy("A healthy result."));
}
return Task.FromResult(
new HealthCheckResult(
context.Registration.FailureStatus, "An unhealthy result."));
}
}
The SampleHealthCheckWithDiConfig
and the Health check needs to be added to the service container :
builder.Services.AddSingleton<SampleHealthCheckWithDiConfig>(new SampleHealthCheckWithDiConfig
{
BaseUriToCheck = new Uri("https://sample.contoso.com/api/")
});
builder.Services.AddHealthChecks()
.AddCheck<SampleHealthCheckWithDI>(
"With Dependency Injection",
tags: new[] { "inject" });
UseHealthChecks vs. MapHealthChecks
There are two ways to make health checks accessible to callers:
- UseHealthChecks registers middleware for handling health checks requests in the middleware pipeline.
- MapHealthChecks registers a health checks endpoint. The endpoint is matched and executed along with other endpoints in the app.
The advantage of using MapHealthChecks
over UseHealthChecks
is the ability to use endpoint aware middleware, such as authorization, and to have greater fine-grained control over the matching policy. The primary advantage of using UseHealthChecks
over MapHealthChecks
is controlling exactly where health checks runs in the middleware pipeline.
- Terminates the pipeline when a request matches the health check endpoint. Short-circuiting is often desirable because it avoids unnecessary work, such as logging and other middleware.
- Is primarily used for configuring the health check middleware in the pipeline.
- Can match any path on a port with a
null
or emptyPathString
. Allows performing a health check on any request made to the specified port. - Source code
MapHealthChecks allows:
- Terminating the pipeline when a request matches the health check endpoint, by calling ShortCircuit. For example,
app.MapHealthChecks("/healthz").ShortCircuit();
. For more information, see Short-circuit middleware after routing. - Mapping specific routes or endpoints for health checks.
- Customization of the URL or path where the health check endpoint is accessible.
- Mapping multiple health check endpoints with different routes or configurations. Multiple endpoint support:
- Enables separate endpoints for different types of health checks or components.
- Is used to differentiate between different aspects of the app's health or apply specific configurations to subsets of health checks.
- Source code
Additional resources
Note
This article was partially created with the help of artificial intelligence. Before publishing, an author reviewed and revised the content as needed. See Our principles for using AI-generated content in Microsoft Learn.
ASP.NET Core offers Health Checks Middleware and libraries for reporting the health of app infrastructure components.
Health checks are exposed by an app as HTTP endpoints. Health check endpoints can be configured for various real-time monitoring scenarios:
- Health probes can be used by container orchestrators and load balancers to check an app's status. For example, a container orchestrator may respond to a failing health check by halting a rolling deployment or restarting a container. A load balancer might react to an unhealthy app by routing traffic away from the failing instance to a healthy instance.
- Use of memory, disk, and other physical server resources can be monitored for healthy status.
- Health checks can test an app's dependencies, such as databases and external service endpoints, to confirm availability and normal functioning.
View or download sample code (how to download)
The sample app includes examples of the scenarios described in this article. To run the sample app for a given scenario, use the dotnet run command from the project's folder in a command shell. See the sample app's README.md
file and the scenario descriptions in this article for details on how to use the sample app.
Prerequisites
Health checks are typically used with an external monitoring service or container orchestrator to check the status of an app. Before adding health checks to an app, decide on which monitoring system to use. The monitoring system dictates what types of health checks to create and how to configure their endpoints.
The Microsoft.AspNetCore.Diagnostics.HealthChecks
package is referenced implicitly for ASP.NET Core apps. To run health checks using Entity Framework Core, add a reference to the Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore
package.
The sample app provides startup code to demonstrate health checks for several scenarios. The database probe scenario checks the health of a database connection using AspNetCore.Diagnostics.HealthChecks
. The DbContext probe scenario checks a database using an EF Core DbContext
. To explore the database scenarios, the sample app:
- Creates a database and provides its connection string in the
appsettings.json
file. - Has the following package references in its project file:
Note
AspNetCore.Diagnostics.HealthChecks
isn't maintained or supported by Microsoft.
Another health check scenario demonstrates how to filter health checks to a management port. The sample app requires you to create a Properties/launchSettings.json
file that includes the management URL and management port. For more information, see the Filter by port section.
Basic health probe
For many apps, a basic health probe configuration that reports the app's availability to process requests (liveness) is sufficient to discover the status of the app.
The basic configuration registers health check services and calls the Health Checks Middleware to respond at a URL endpoint with a health response. By default, no specific health checks are registered to test any particular dependency or subsystem. The app is considered healthy if it can respond at the health endpoint URL. The default response writer writes the status (HealthStatus) as a plaintext response back to the client, indicating either a HealthStatus.Healthy, HealthStatus.Degraded, or HealthStatus.Unhealthy status.
Register health check services with AddHealthChecks in Startup.ConfigureServices
. Create a health check endpoint by calling MapHealthChecks
in Startup.Configure
.
In the sample app, the health check endpoint is created at /health
(BasicStartup.cs
):
public class BasicStartup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks();
}
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health");
});
}
}
To run the basic configuration scenario using the sample app, execute the following command from the project's folder in a command shell:
dotnet run --scenario basic
Docker example
Docker offers a built-in HEALTHCHECK
directive that can be used to check the status of an app that uses the basic health check configuration:
HEALTHCHECK CMD curl --fail http://localhost:5000/health || exit
Create health checks
Health checks are created by implementing the IHealthCheck interface. The CheckHealthAsync method returns a HealthCheckResult that indicates the health as Healthy
, Degraded
, or Unhealthy
. The result is written as a plaintext response with a configurable status code (configuration is described in the Health check options section). HealthCheckResult can also return optional key-value pairs.
The following ExampleHealthCheck
class demonstrates the layout of a health check. The health checks logic is placed in the CheckHealthAsync
method. The following example sets a dummy variable, healthCheckResultHealthy
, to true
. If the value of healthCheckResultHealthy
is set to false
, the HealthCheckRegistration.FailureStatus status is returned.
public class ExampleHealthCheck : IHealthCheck
{
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default(CancellationToken))
{
var healthCheckResultHealthy = true;
if (healthCheckResultHealthy)
{
return Task.FromResult(
HealthCheckResult.Healthy("A healthy result."));
}
return Task.FromResult(
new HealthCheckResult(context.Registration.FailureStatus,
"An unhealthy result."));
}
}
If CheckHealthAsync throws an exception during the check, a new HealthReportEntry is returned with its HealthReportEntry.Status set to the FailureStatus, which is defined by AddCheck (see the Register health check services section) and includes the inner exception that initially caused the check failure. The Description is set to the exception's message.
Register health check services
The ExampleHealthCheck
type is added to health check services with AddCheck in Startup.ConfigureServices
:
services.AddHealthChecks()
.AddCheck<ExampleHealthCheck>("example_health_check");
The AddCheck overload shown in the following example sets the failure status (HealthStatus) to report when the health check reports a failure. If the failure status is set to null
(default), HealthStatus.Unhealthy is reported. This overload is a useful scenario for library authors, where the failure status indicated by the library is enforced by the app when a health check failure occurs if the health check implementation honors the setting.
Tags can be used to filter health checks (described further in the Filter health checks section).
services.AddHealthChecks()
.AddCheck<ExampleHealthCheck>(
"example_health_check",
failureStatus: HealthStatus.Degraded,
tags: new[] { "example" });
AddCheck can also execute a lambda function. In the following example, the health check name is specified as Example
and the check always returns a healthy state:
services.AddHealthChecks()
.AddCheck("Example", () =>
HealthCheckResult.Healthy("Example is OK!"), tags: new[] { "example" });
Call AddTypeActivatedCheck to pass arguments to a health check implementation. In the following example, TestHealthCheckWithArgs
accepts an integer and a string for use when CheckHealthAsync is called:
private class TestHealthCheckWithArgs : IHealthCheck
{
public TestHealthCheckWithArgs(int i, string s)
{
I = i;
S = s;
}
public int I { get; set; }
public string S { get; set; }
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context,
CancellationToken cancellationToken = default)
{
...
}
}
TestHealthCheckWithArgs
is registered by calling AddTypeActivatedCheck
with the integer and string passed to the implementation:
services.AddHealthChecks()
.AddTypeActivatedCheck<TestHealthCheckWithArgs>(
"test",
failureStatus: HealthStatus.Degraded,
tags: new[] { "example" },
args: new object[] { 5, "string" });
Use Health Checks Routing
In Startup.Configure
, call MapHealthChecks
on the endpoint builder with the endpoint URL or relative path:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health");
});
Require host
Call RequireHost
to specify one or more permitted hosts for the health check endpoint. Hosts should be Unicode rather than punycode and may include a port. If a collection isn't supplied, any host is accepted.
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health").RequireHost("www.contoso.com:5001");
});
For more information, see the Filter by port section.
Require authorization
Call RequireAuthorization
to run Authorization Middleware on the health check request endpoint. A RequireAuthorization
overload accepts one or more authorization policies. If a policy isn't provided, the default authorization policy is used.
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health").RequireAuthorization();
});
Enable Cross-Origin Requests (CORS)
Although running health checks manually from a browser isn't a common use scenario, CORS Middleware can be enabled by calling RequireCors
on health checks endpoints. A RequireCors
overload accepts a CORS policy builder delegate (CorsPolicyBuilder
) or a policy name. If a policy isn't provided, the default CORS policy is used. For more information, see Enable Cross-Origin Requests (CORS) in ASP.NET Core.
Health check options
HealthCheckOptions provide an opportunity to customize health check behavior:
Filter health checks
By default, Health Checks Middleware runs all registered health checks. To run a subset of health checks, provide a function that returns a boolean to the Predicate option. In the following example, the Bar
health check is filtered out by its tag (bar_tag
) in the function's conditional statement, where true
is only returned if the health check's Tags property matches foo_tag
or baz_tag
:
In Startup.ConfigureServices
:
services.AddHealthChecks()
.AddCheck("Foo", () =>
HealthCheckResult.Healthy("Foo is OK!"), tags: new[] { "foo_tag" })
.AddCheck("Bar", () =>
HealthCheckResult.Unhealthy("Bar is unhealthy!"), tags: new[] { "bar_tag" })
.AddCheck("Baz", () =>
HealthCheckResult.Healthy("Baz is OK!"), tags: new[] { "baz_tag" });
In Startup.Configure
, the Predicate
filters out the 'Bar' health check. Only Foo and Baz execute:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{
Predicate = (check) => check.Tags.Contains("foo_tag") ||
check.Tags.Contains("baz_tag")
});
});
Customize the HTTP status code
Use ResultStatusCodes to customize the mapping of health status to HTTP status codes. The following StatusCodes assignments are the default values used by the middleware. Change the status code values to meet your requirements.
In Startup.Configure
:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{
ResultStatusCodes =
{
[HealthStatus.Healthy] = StatusCodes.Status200OK,
[HealthStatus.Degraded] = StatusCodes.Status200OK,
[HealthStatus.Unhealthy] = StatusCodes.Status503ServiceUnavailable
}
});
});
Suppress cache headers
AllowCachingResponses controls whether the Health Checks Middleware adds HTTP headers to a probe response to prevent response caching. If the value is false
(default), the middleware sets or overrides the Cache-Control
, Expires
, and Pragma
headers to prevent response caching. If the value is true
, the middleware doesn't modify the cache headers of the response.
In Startup.Configure
:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{
AllowCachingResponses = true
});
});
Customize output
In Startup.Configure
, set the HealthCheckOptions.ResponseWriter option to a delegate for writing the response:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{
ResponseWriter = WriteResponse
});
});
The default delegate writes a minimal plaintext response with the string value of HealthReport.Status
. The following custom delegates output a custom JSON response.
The first example from the sample app demonstrates how to use System.Text.Json:
private static Task WriteResponse(HttpContext context, HealthReport result)
{
context.Response.ContentType = "application/json; charset=utf-8";
var options = new JsonWriterOptions
{
Indented = true
};
using (var stream = new MemoryStream())
{
using (var writer = new Utf8JsonWriter(stream, options))
{
writer.WriteStartObject();
writer.WriteString("status", result.Status.ToString());
writer.WriteStartObject("results");
foreach (var entry in result.Entries)
{
writer.WriteStartObject(entry.Key);
writer.WriteString("status", entry.Value.Status.ToString());
writer.WriteString("description", entry.Value.Description);
writer.WriteStartObject("data");
foreach (var item in entry.Value.Data)
{
writer.WritePropertyName(item.Key);
JsonSerializer.Serialize(
writer, item.Value, item.Value?.GetType() ??
typeof(object));
}
writer.WriteEndObject();
writer.WriteEndObject();
}
writer.WriteEndObject();
writer.WriteEndObject();
}
var json = Encoding.UTF8.GetString(stream.ToArray());
return context.Response.WriteAsync(json);
}
}
The second example demonstrates how to use Newtonsoft.Json
:
private static Task WriteResponse(HttpContext context, HealthReport result)
{
context.Response.ContentType = "application/json";
var json = new JObject(
new JProperty("status", result.Status.ToString()),
new JProperty("results", new JObject(result.Entries.Select(pair =>
new JProperty(pair.Key, new JObject(
new JProperty("status", pair.Value.Status.ToString()),
new JProperty("description", pair.Value.Description),
new JProperty("data", new JObject(pair.Value.Data.Select(
p => new JProperty(p.Key, p.Value))))))))));
return context.Response.WriteAsync(
json.ToString(Formatting.Indented));
}
In the sample app, comment out the SYSTEM_TEXT_JSON
preprocessor directive in CustomWriterStartup.cs
to enable the Newtonsoft.Json
version of WriteResponse
.
The health checks API doesn't provide built-in support for complex JSON return formats because the format is specific to your choice of monitoring system. Customize the response in the preceding examples as needed. For more information on JSON serialization with System.Text.Json
, see How to serialize and deserialize JSON in .NET.
Database probe
A health check can specify a database query to run as a boolean test to indicate if the database is responding normally.
The sample app uses AspNetCore.Diagnostics.HealthChecks
, a health check library for ASP.NET Core apps, to run a health check on a SQL Server database. AspNetCore.Diagnostics.HealthChecks
executes a SELECT 1
query against the database to confirm the connection to the database is healthy.
Warning
When checking a database connection with a query, choose a query that returns quickly. The query approach runs the risk of overloading the database and degrading its performance. In most cases, running a test query isn't necessary. Merely making a successful connection to the database is sufficient. If you find it necessary to run a query, choose a simple SELECT query, such as SELECT 1
.
Include a package reference to AspNetCore.HealthChecks.SqlServer
.
Supply a valid database connection string in the appsettings.json
file of the sample app. The app uses a SQL Server database named HealthCheckSample
:
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=HealthCheckSample;Trusted_Connection=True;MultipleActiveResultSets=true;ConnectRetryCount=0"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"Console": {
"IncludeScopes": "true"
}
},
"AllowedHosts": "*"
}
Register health check services with AddHealthChecks in Startup.ConfigureServices
. The sample app calls the AddSqlServer
method with the database's connection string (DbHealthStartup.cs
):
services.AddHealthChecks()
.AddSqlServer(Configuration["ConnectionStrings:DefaultConnection"]);
A health check endpoint is created by calling MapHealthChecks
in Startup.Configure
:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health");
}
To run the database probe scenario using the sample app, execute the following command from the project's folder in a command shell:
dotnet run --scenario db
Note
AspNetCore.Diagnostics.HealthChecks
isn't maintained or supported by Microsoft.
Entity Framework Core DbContext probe
The DbContext
check confirms that the app can communicate with the database configured for an EF Core DbContext
. The DbContext
check is supported in apps that:
- Use Entity Framework (EF) Core.
- Include a package reference to
Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore
.
AddDbContextCheck<TContext>
registers a health check for a DbContext
. The DbContext
is supplied as the TContext
to the method. An overload is available to configure the failure status, tags, and a custom test query.
By default:
- The
DbContextHealthCheck
calls EF Core'sCanConnectAsync
method. You can customize what operation is run when checking health usingAddDbContextCheck
method overloads. - The name of the health check is the name of the
TContext
type.
In the sample app, AppDbContext
is provided to AddDbContextCheck
and registered as a service in Startup.ConfigureServices
(DbContextHealthStartup.cs
):
services.AddHealthChecks()
.AddDbContextCheck<AppDbContext>();
services.AddDbContext<AppDbContext>(options =>
{
options.UseSqlServer(
Configuration["ConnectionStrings:DefaultConnection"]);
});
A health check endpoint is created by calling MapHealthChecks
in Startup.Configure
:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health");
}
To run the DbContext
probe scenario using the sample app, confirm that the database specified by the connection string doesn't exist in the SQL Server instance. If the database exists, delete it.
Execute the following command from the project's folder in a command shell:
dotnet run --scenario dbcontext
After the app is running, check the health status by making a request to the /health
endpoint in a browser. The database and AppDbContext
don't exist, so app provides the following response:
Unhealthy
Trigger the sample app to create the database. Make a request to /createdatabase
. The app responds:
Creating the database...
Done!
Navigate to /health to see the health status.
Make a request to the /health
endpoint. The database and context exist, so app responds:
Healthy
Trigger the sample app to delete the database. Make a request to /deletedatabase
. The app responds:
Deleting the database...
Done!
Navigate to /health to see the health status.
Make a request to the /health
endpoint. The app provides an unhealthy response:
Unhealthy
Separate readiness and liveness probes
In some hosting scenarios, a pair of health checks is used to distinguish two app states:
- Readiness indicates if the app is running normally but isn't ready to receive requests.
- Liveness indicates if an app has crashed and must be restarted.
Consider the following example: An app must download a large configuration file before it's ready to process requests. We don't want the app to be restarted if the initial download fails because the app can retry downloading the file several times. We use a liveness probe to describe the liveness of the process, no other checks are run. We also want to prevent requests from being sent to the app before the configuration file download has succeeded. We use a readiness probe to indicate a "not ready" state until the download succeeds and the app is ready to receive requests.
The sample app contains a health check to report the completion of long-running startup task in a Hosted Service. The StartupHostedServiceHealthCheck
exposes a property, StartupTaskCompleted
, that the hosted service can set to true
when its long-running task is finished (StartupHostedServiceHealthCheck.cs
):
public class StartupHostedServiceHealthCheck : IHealthCheck
{
private volatile bool _startupTaskCompleted = false;
public string Name => "slow_dependency_check";
public bool StartupTaskCompleted
{
get => _startupTaskCompleted;
set => _startupTaskCompleted = value;
}
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default(CancellationToken))
{
if (StartupTaskCompleted)
{
return Task.FromResult(
HealthCheckResult.Healthy("The startup task is finished."));
}
return Task.FromResult(
HealthCheckResult.Unhealthy("The startup task is still running."));
}
}
The long-running background task is started by a Hosted Service (Services/StartupHostedService
). At the conclusion of the task, StartupHostedServiceHealthCheck.StartupTaskCompleted
is set to true
:
public class StartupHostedService : IHostedService, IDisposable
{
private readonly int _delaySeconds = 15;
private readonly ILogger _logger;
private readonly StartupHostedServiceHealthCheck _startupHostedServiceHealthCheck;
public StartupHostedService(ILogger<StartupHostedService> logger,
StartupHostedServiceHealthCheck startupHostedServiceHealthCheck)
{
_logger = logger;
_startupHostedServiceHealthCheck = startupHostedServiceHealthCheck;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Startup Background Service is starting.");
// Simulate the effect of a long-running startup task.
Task.Run(async () =>
{
await Task.Delay(_delaySeconds * 1000);
_startupHostedServiceHealthCheck.StartupTaskCompleted = true;
_logger.LogInformation("Startup Background Service has started.");
});
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Startup Background Service is stopping.");
return Task.CompletedTask;
}
public void Dispose()
{
}
}
The health check is registered with AddCheck in Startup.ConfigureServices
along with the hosted service. Because the hosted service must set the property on the health check, the health check is also registered in the service container (LivenessProbeStartup.cs
):
services.AddHostedService<StartupHostedService>();
services.AddSingleton<StartupHostedServiceHealthCheck>();
services.AddHealthChecks()
.AddCheck<StartupHostedServiceHealthCheck>(
"hosted_service_startup",
failureStatus: HealthStatus.Degraded,
tags: new[] { "ready" });
services.Configure<HealthCheckPublisherOptions>(options =>
{
options.Delay = TimeSpan.FromSeconds(2);
options.Predicate = (check) => check.Tags.Contains("ready");
});
services.AddSingleton<IHealthCheckPublisher, ReadinessPublisher>();
A health check endpoint is created by calling MapHealthChecks
in Startup.Configure
. In the sample app, the health check endpoints are created at:
/health/ready
for the readiness check. The readiness check filters health checks to the health check with theready
tag./health/live
for the liveness check. The liveness check filters out theStartupHostedServiceHealthCheck
by returningfalse
in theHealthCheckOptions.Predicate
(for more information, see Filter health checks)
In the following example code:
- The readiness check uses all registered checks with the 'ready' tag.
- The
Predicate
excludes all checks and returns a 200-Ok.
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health/ready", new HealthCheckOptions()
{
Predicate = (check) => check.Tags.Contains("ready"),
});
endpoints.MapHealthChecks("/health/live", new HealthCheckOptions()
{
Predicate = (_) => false
});
}
To run the readiness/liveness configuration scenario using the sample app, execute the following command from the project's folder in a command shell:
dotnet run --scenario liveness
In a browser, visit /health/ready
several times until 15 seconds have passed. The health check reports Unhealthy
for the first 15 seconds. After 15 seconds, the endpoint reports Healthy
, which reflects the completion of the long-running task by the hosted service.
This example also creates a Health Check Publisher (IHealthCheckPublisher implementation) that runs the first readiness check with a two-second delay. For more information, see the Health Check Publisher section.
Kubernetes example
Using separate readiness and liveness checks is useful in an environment such as Kubernetes. In Kubernetes, an app might be required to run time-consuming startup work before accepting requests, such as a test of the underlying database availability. Using separate checks allows the orchestrator to distinguish whether the app is functioning but not yet ready or if the app has failed to start. For more information on readiness and liveness probes in Kubernetes, see Configure Liveness and Readiness Probes in the Kubernetes documentation.
The following example demonstrates a Kubernetes readiness probe configuration:
spec:
template:
spec:
readinessProbe:
# an http probe
httpGet:
path: /health/ready
port: 80
# length of time to wait for a pod to initialize
# after pod startup, before applying health checking
initialDelaySeconds: 30
timeoutSeconds: 1
ports:
- containerPort: 80
Metric-based probe with a custom response writer
The sample app demonstrates a memory health check with a custom response writer.
MemoryHealthCheck
reports a degraded status if the app uses more than a given threshold of memory (1 GB in the sample app). The HealthCheckResult includes Garbage Collector (GC) information for the app (MemoryHealthCheck.cs
):
public class MemoryHealthCheck : IHealthCheck
{
private readonly IOptionsMonitor<MemoryCheckOptions> _options;
public MemoryHealthCheck(IOptionsMonitor<MemoryCheckOptions> options)
{
_options = options;
}
public string Name => "memory_check";
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default(CancellationToken))
{
var options = _options.Get(context.Registration.Name);
// Include GC information in the reported diagnostics.
var allocated = GC.GetTotalMemory(forceFullCollection: false);
var data = new Dictionary<string, object>()
{
{ "AllocatedBytes", allocated },
{ "Gen0Collections", GC.CollectionCount(0) },
{ "Gen1Collections", GC.CollectionCount(1) },
{ "Gen2Collections", GC.CollectionCount(2) },
};
var status = (allocated < options.Threshold) ?
HealthStatus.Healthy : context.Registration.FailureStatus;
return Task.FromResult(new HealthCheckResult(
status,
description: "Reports degraded status if allocated bytes " +
$">= {options.Threshold} bytes.",
exception: null,
data: data));
}
}
Register health check services with AddHealthChecks in Startup.ConfigureServices
. Instead of enabling the health check by passing it to AddCheck, the MemoryHealthCheck
is registered as a service. All IHealthCheck registered services are available to the health check services and middleware. We recommend registering health check services as Singleton services.
In CustomWriterStartup.cs
of the sample app:
services.AddHealthChecks()
.AddMemoryHealthCheck("memory");
A health check endpoint is created by calling MapHealthChecks
in Startup.Configure
. A WriteResponse
delegate is provided to the ResponseWriter property to output a custom JSON response when the health check executes:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{
ResponseWriter = WriteResponse
});
}
The WriteResponse
delegate formats the CompositeHealthCheckResult
into a JSON object and yields JSON output for the health check response. For more information, see the Customize output section.
To run the metric-based probe with custom response writer output using the sample app, execute the following command from the project's folder in a command shell:
dotnet run --scenario writer
Note
AspNetCore.Diagnostics.HealthChecks
includes metric-based health check scenarios, including disk storage and maximum value liveness checks.
AspNetCore.Diagnostics.HealthChecks
isn't maintained or supported by Microsoft.
Filter by port
Call RequireHost
on MapHealthChecks
with a URL pattern that specifies a port to restrict health check requests to the port specified. This approach is typically used in a container environment to expose a port for monitoring services.
The sample app configures the port using the Environment Variable Configuration Provider. The port is set in the launchSettings.json
file and passed to the configuration provider via an environment variable. You must also configure the server to listen to requests on the management port.
To use the sample app to demonstrate management port configuration, create the launchSettings.json
file in a Properties
folder.
The following Properties/launchSettings.json
file in the sample app isn't included in the sample app's project files and must be created manually:
{
"profiles": {
"SampleApp": {
"commandName": "Project",
"commandLineArgs": "",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_URLS": "http://localhost:5000/;http://localhost:5001/",
"ASPNETCORE_MANAGEMENTPORT": "5001"
},
"applicationUrl": "http://localhost:5000/"
}
}
}
Register health check services with AddHealthChecks in Startup.ConfigureServices
. Create a health check endpoint by calling MapHealthChecks
in Startup.Configure
.
In the sample app, a call to RequireHost
on the endpoint in Startup.Configure
specifies the management port from configuration:
endpoints.MapHealthChecks("/health")
.RequireHost($"*:{Configuration["ManagementPort"]}");
Endpoints are created in the sample app in Startup.Configure
. In the following example code:
- The readiness check uses all registered checks with the 'ready' tag.
- The
Predicate
excludes all checks and returns a 200-Ok.
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health/ready", new HealthCheckOptions()
{
Predicate = (check) => check.Tags.Contains("ready"),
});
endpoints.MapHealthChecks("/health/live", new HealthCheckOptions()
{
Predicate = (_) => false
});
}
Note
You can avoid creating the launchSettings.json
file in the sample app by setting the management port explicitly in code. In Program.cs
where the HostBuilder is created, add a call to ListenAnyIP and provide the app's management port endpoint. In Configure
of ManagementPortStartup.cs
, specify the management port with RequireHost
:
Program.cs
:
return new HostBuilder()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseKestrel()
.ConfigureKestrel(serverOptions =>
{
serverOptions.ListenAnyIP(5001);
})
.UseStartup(startupType);
})
.Build();
ManagementPortStartup.cs
:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health").RequireHost("*:5001");
});
To run the management port configuration scenario using the sample app, execute the following command from the project's folder in a command shell:
dotnet run --scenario port
Distribute a health check library
To distribute a health check as a library:
Write a health check that implements the IHealthCheck interface as a standalone class. The class can rely on dependency injection (DI), type activation, and named options to access configuration data.
In the health checks logic of
CheckHealthAsync
:data1
anddata2
are used in the method to run the probe's health check logic.AccessViolationException
is handled.
When an AccessViolationException occurs, the FailureStatus is returned with the HealthCheckResult to allow users to configure the health checks failure status.
using System; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Diagnostics.HealthChecks; namespace SampleApp { public class ExampleHealthCheck : IHealthCheck { private readonly string _data1; private readonly int? _data2; public ExampleHealthCheck(string data1, int? data2) { _data1 = data1 ?? throw new ArgumentNullException(nameof(data1)); _data2 = data2 ?? throw new ArgumentNullException(nameof(data2)); } public async Task<HealthCheckResult> CheckHealthAsync( HealthCheckContext context, CancellationToken cancellationToken) { try { return HealthCheckResult.Healthy(); } catch (AccessViolationException ex) { return new HealthCheckResult( context.Registration.FailureStatus, description: "An access violation occurred during the check.", exception: ex, data: null); } } } }
Write an extension method with parameters that the consuming app calls in its
Startup.Configure
method. In the following example, assume the following health check method signature:ExampleHealthCheck(string, string, int )
The preceding signature indicates that the
ExampleHealthCheck
requires additional data to process the health check probe logic. The data is provided to the delegate used to create the health check instance when the health check is registered with an extension method. In the following example, the caller specifies optional:- health check name (
name
). Ifnull
,example_health_check
is used. - string data point for the health check (
data1
). - integer data point for the health check (
data2
). Ifnull
,1
is used. - failure status (HealthStatus). The default is
null
. Ifnull
, HealthStatus.Unhealthy is reported for a failure status. - tags (
IEnumerable<string>
).
using System.Collections.Generic; using Microsoft.Extensions.Diagnostics.HealthChecks; public static class ExampleHealthCheckBuilderExtensions { const string DefaultName = "example_health_check"; public static IHealthChecksBuilder AddExampleHealthCheck( this IHealthChecksBuilder builder, string name = default, string data1, int data2 = 1, HealthStatus? failureStatus = default, IEnumerable<string> tags = default) { return builder.Add(new HealthCheckRegistration( name ?? DefaultName, sp => new ExampleHealthCheck(data1, data2), failureStatus, tags)); } }
- health check name (
Health Check Publisher
When an IHealthCheckPublisher is added to the service container, the health check system periodically executes your health checks and calls PublishAsync
with the result. This is useful in a push-based health monitoring system scenario that expects each process to call the monitoring system periodically in order to determine health.
The IHealthCheckPublisher interface has a single method:
Task PublishAsync(HealthReport report, CancellationToken cancellationToken);
HealthCheckPublisherOptions allow you to set:
- Delay: The initial delay applied after the app starts before executing IHealthCheckPublisher instances. The delay is applied once at startup and doesn't apply to subsequent iterations. The default value is five seconds.
- Period: The period of IHealthCheckPublisher execution. The default value is 30 seconds.
- Predicate: If Predicate is
null
(default), the health check publisher service runs all registered health checks. To run a subset of health checks, provide a function that filters the set of checks. The predicate is evaluated each period. - Timeout: The timeout for executing the health checks for all IHealthCheckPublisher instances. Use InfiniteTimeSpan to execute without a timeout. The default value is 30 seconds.
In the sample app, ReadinessPublisher
is an IHealthCheckPublisher implementation. The health check status is logged for each check at a log level of:
- Information (LogInformation) if the health checks status is Healthy.
- Error (LogError) if the status is either Degraded or Unhealthy.
public class ReadinessPublisher : IHealthCheckPublisher
{
private readonly ILogger _logger;
public ReadinessPublisher(ILogger<ReadinessPublisher> logger)
{
_logger = logger;
}
// The following example is for demonstration purposes only. Health Checks
// Middleware already logs health checks results. A real-world readiness
// check in a production app might perform a set of more expensive or
// time-consuming checks to determine if other resources are responding
// properly.
public Task PublishAsync(HealthReport report,
CancellationToken cancellationToken)
{
if (report.Status == HealthStatus.Healthy)
{
_logger.LogInformation("{Timestamp} Readiness Probe Status: {Result}",
DateTime.UtcNow, report.Status);
}
else
{
_logger.LogError("{Timestamp} Readiness Probe Status: {Result}",
DateTime.UtcNow, report.Status);
}
cancellationToken.ThrowIfCancellationRequested();
return Task.CompletedTask;
}
}
In the sample app's LivenessProbeStartup
example, the StartupHostedService
readiness check has a two second startup delay and runs the check every 30 seconds. To activate the IHealthCheckPublisher implementation, the sample registers ReadinessPublisher
as a singleton service in the dependency injection (DI) container:
services.AddHostedService<StartupHostedService>();
services.AddSingleton<StartupHostedServiceHealthCheck>();
services.AddHealthChecks()
.AddCheck<StartupHostedServiceHealthCheck>(
"hosted_service_startup",
failureStatus: HealthStatus.Degraded,
tags: new[] { "ready" });
services.Configure<HealthCheckPublisherOptions>(options =>
{
options.Delay = TimeSpan.FromSeconds(2);
options.Predicate = (check) => check.Tags.Contains("ready");
});
services.AddSingleton<IHealthCheckPublisher, ReadinessPublisher>();
Note
AspNetCore.Diagnostics.HealthChecks
includes publishers for several systems, including Application Insights.
AspNetCore.Diagnostics.HealthChecks
isn't maintained or supported by Microsoft.
Restrict health checks with MapWhen
Use MapWhen to conditionally branch the request pipeline for health check endpoints.
In the following example, MapWhen
branches the request pipeline to activate Health Checks Middleware if a GET request is received for the api/HealthCheck
endpoint:
app.MapWhen(
context => context.Request.Method == HttpMethod.Get.Method &&
context.Request.Path.StartsWith("/api/HealthCheck"),
builder => builder.UseHealthChecks());
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
For more information, see ASP.NET Core Middleware.
ASP.NET Core offers Health Checks Middleware and libraries for reporting the health of app infrastructure components.
Health checks are exposed by an app as HTTP endpoints. Health check endpoints can be configured for various real-time monitoring scenarios:
- Health probes can be used by container orchestrators and load balancers to check an app's status. For example, a container orchestrator may respond to a failing health check by halting a rolling deployment or restarting a container. A load balancer might react to an unhealthy app by routing traffic away from the failing instance to a healthy instance.
- Use of memory, disk, and other physical server resources can be monitored for healthy status.
- Health checks can test an app's dependencies, such as databases and external service endpoints, to confirm availability and normal functioning.
View or download sample code (how to download)
The sample app includes examples of the scenarios described in this article. To run the sample app for a given scenario, use the dotnet run command from the project's folder in a command shell. See the sample app's README.md
file and the scenario descriptions in this article for details on how to use the sample app.
Prerequisites
Health checks are typically used with an external monitoring service or container orchestrator to check the status of an app. Before adding health checks to an app, decide on which monitoring system to use. The monitoring system dictates what types of health checks to create and how to configure their endpoints.
The Microsoft.AspNetCore.Diagnostics.HealthChecks
package is referenced implicitly for ASP.NET Core apps. To run health checks using Entity Framework Core, add a package reference to the Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore
package.
The sample app provides startup code to demonstrate health checks for several scenarios. The database probe scenario checks the health of a database connection using AspNetCore.Diagnostics.HealthChecks
. The DbContext probe scenario checks a database using an EF Core DbContext
. To explore the database scenarios, the sample app:
- Creates a database and provides its connection string in the
appsettings.json
file. - Has the following package references in its project file:
Note
AspNetCore.Diagnostics.HealthChecks
isn't maintained or supported by Microsoft.
Another health check scenario demonstrates how to filter health checks to a management port. The sample app requires you to create a Properties/launchSettings.json
file that includes the management URL and management port. For more information, see the Filter by port section.
Basic health probe
For many apps, a basic health probe configuration that reports the app's availability to process requests (liveness) is sufficient to discover the status of the app.
The basic configuration registers health check services and calls the Health Checks Middleware to respond at a URL endpoint with a health response. By default, no specific health checks are registered to test any particular dependency or subsystem. The app is considered healthy if it can respond at the health endpoint URL. The default response writer writes the status (HealthStatus) as a plaintext response back to the client, indicating either a HealthStatus.Healthy, HealthStatus.Degraded, or HealthStatus.Unhealthy status.
Register health check services with AddHealthChecks in Startup.ConfigureServices
. Create a health check endpoint by calling MapHealthChecks
in Startup.Configure
.
In the sample app, the health check endpoint is created at /health
(BasicStartup.cs
):
public class BasicStartup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks();
}
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health");
});
}
}
To run the basic configuration scenario using the sample app, execute the following command from the project's folder in a command shell:
dotnet run --scenario basic
Docker example
Docker offers a built-in HEALTHCHECK
directive that can be used to check the status of an app that uses the basic health check configuration:
HEALTHCHECK CMD curl --fail http://localhost:5000/health || exit
Create health checks
Health checks are created by implementing the IHealthCheck interface. The CheckHealthAsync method returns a HealthCheckResult that indicates the health as Healthy
, Degraded
, or Unhealthy
. The result is written as a plaintext response with a configurable status code (configuration is described in the Health check options section). HealthCheckResult can also return optional key-value pairs.
The following ExampleHealthCheck
class demonstrates the layout of a health check. The health checks logic is placed in the CheckHealthAsync
method. The following example sets a dummy variable, healthCheckResultHealthy
, to true
. If the value of healthCheckResultHealthy
is set to false
, the HealthCheckResult.Unhealthy
status is returned.
public class ExampleHealthCheck : IHealthCheck
{
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default(CancellationToken))
{
var healthCheckResultHealthy = true;
if (healthCheckResultHealthy)
{
return Task.FromResult(
HealthCheckResult.Healthy("A healthy result."));
}
return Task.FromResult(
HealthCheckResult.Unhealthy("An unhealthy result."));
}
}
Register health check services
The ExampleHealthCheck
type is added to health check services with AddCheck in Startup.ConfigureServices
:
services.AddHealthChecks()
.AddCheck<ExampleHealthCheck>("example_health_check");
The AddCheck overload shown in the following example sets the failure status (HealthStatus) to report when the health check reports a failure. If the failure status is set to null
(default), HealthStatus.Unhealthy is reported. This overload is a useful scenario for library authors, where the failure status indicated by the library is enforced by the app when a health check failure occurs if the health check implementation honors the setting.
Tags can be used to filter health checks (described further in the Filter health checks section).
services.AddHealthChecks()
.AddCheck<ExampleHealthCheck>(
"example_health_check",
failureStatus: HealthStatus.Degraded,
tags: new[] { "example" });
AddCheck can also execute a lambda function. In the following example, the health check name is specified as Example
and the check always returns a healthy state:
services.AddHealthChecks()
.AddCheck("Example", () =>
HealthCheckResult.Healthy("Example is OK!"), tags: new[] { "example" });
Call AddTypeActivatedCheck to pass arguments to a health check implementation. In the following example, TestHealthCheckWithArgs
accepts an integer and a string for use when CheckHealthAsync is called:
private class TestHealthCheckWithArgs : IHealthCheck
{
public TestHealthCheckWithArgs(int i, string s)
{
I = i;
S = s;
}
public int I { get; set; }
public string S { get; set; }
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context,
CancellationToken cancellationToken = default)
{
...
}
}
TestHealthCheckWithArgs
is registered by calling AddTypeActivatedCheck
with the integer and string passed to the implementation:
services.AddHealthChecks()
.AddTypeActivatedCheck<TestHealthCheckWithArgs>(
"test",
failureStatus: HealthStatus.Degraded,
tags: new[] { "example" },
args: new object[] { 5, "string" });
Use Health Checks Routing
In Startup.Configure
, call MapHealthChecks
on the endpoint builder with the endpoint URL or relative path:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health");
});
Require host
Call RequireHost
to specify one or more permitted hosts for the health check endpoint. Hosts should be Unicode rather than punycode and may include a port. If a collection isn't supplied, any host is accepted.
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health").RequireHost("www.contoso.com:5001");
});
For more information, see the Filter by port section.
Require authorization
Call RequireAuthorization
to run Authorization Middleware on the health check request endpoint. A RequireAuthorization
overload accepts one or more authorization policies. If a policy isn't provided, the default authorization policy is used.
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health").RequireAuthorization();
});
Enable Cross-Origin Requests (CORS)
Although running health checks manually from a browser isn't a common use scenario, CORS Middleware can be enabled by calling RequireCors
on health checks endpoints. A RequireCors
overload accepts a CORS policy builder delegate (CorsPolicyBuilder
) or a policy name. If a policy isn't provided, the default CORS policy is used. For more information, see Enable Cross-Origin Requests (CORS) in ASP.NET Core.
Health check options
HealthCheckOptions provide an opportunity to customize health check behavior:
Filter health checks
By default, Health Checks Middleware runs all registered health checks. To run a subset of health checks, provide a function that returns a boolean to the Predicate option. In the following example, the Bar
health check is filtered out by its tag (bar_tag
) in the function's conditional statement, where true
is only returned if the health check's Tags property matches foo_tag
or baz_tag
:
In Startup.ConfigureServices
:
services.AddHealthChecks()
.AddCheck("Foo", () =>
HealthCheckResult.Healthy("Foo is OK!"), tags: new[] { "foo_tag" })
.AddCheck("Bar", () =>
HealthCheckResult.Unhealthy("Bar is unhealthy!"), tags: new[] { "bar_tag" })
.AddCheck("Baz", () =>
HealthCheckResult.Healthy("Baz is OK!"), tags: new[] { "baz_tag" });
In Startup.Configure
, the Predicate
filters out the 'Bar' health check. Only Foo and Baz execute:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{
Predicate = (check) => check.Tags.Contains("foo_tag") ||
check.Tags.Contains("baz_tag")
});
});
Customize the HTTP status code
Use ResultStatusCodes to customize the mapping of health status to HTTP status codes. The following StatusCodes assignments are the default values used by the middleware. Change the status code values to meet your requirements.
In Startup.Configure
:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{
ResultStatusCodes =
{
[HealthStatus.Healthy] = StatusCodes.Status200OK,
[HealthStatus.Degraded] = StatusCodes.Status200OK,
[HealthStatus.Unhealthy] = StatusCodes.Status503ServiceUnavailable
}
});
});
Suppress cache headers
AllowCachingResponses controls whether the Health Checks Middleware adds HTTP headers to a probe response to prevent response caching. If the value is false
(default), the middleware sets or overrides the Cache-Control
, Expires
, and Pragma
headers to prevent response caching. If the value is true
, the middleware doesn't modify the cache headers of the response.
In Startup.Configure
:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{
AllowCachingResponses = true
});
});
Customize output
In Startup.Configure
, set the HealthCheckOptions.ResponseWriter option to a delegate for writing the response:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{
ResponseWriter = WriteResponse
});
});
The default delegate writes a minimal plaintext response with the string value of HealthReport.Status
. The following custom delegates output a custom JSON response.
The first example from the sample app demonstrates how to use System.Text.Json:
private static Task WriteResponse(HttpContext context, HealthReport result)
{
context.Response.ContentType = "application/json; charset=utf-8";
var options = new JsonWriterOptions
{
Indented = true
};
using (var stream = new MemoryStream())
{
using (var writer = new Utf8JsonWriter(stream, options))
{
writer.WriteStartObject();
writer.WriteString("status", result.Status.ToString());
writer.WriteStartObject("results");
foreach (var entry in result.Entries)
{
writer.WriteStartObject(entry.Key);
writer.WriteString("status", entry.Value.Status.ToString());
writer.WriteString("description", entry.Value.Description);
writer.WriteStartObject("data");
foreach (var item in entry.Value.Data)
{
writer.WritePropertyName(item.Key);
JsonSerializer.Serialize(
writer, item.Value, item.Value?.GetType() ??
typeof(object));
}
writer.WriteEndObject();
writer.WriteEndObject();
}
writer.WriteEndObject();
writer.WriteEndObject();
}
var json = Encoding.UTF8.GetString(stream.ToArray());
return context.Response.WriteAsync(json);
}
}
The second example demonstrates how to use Newtonsoft.Json:
private static Task WriteResponse(HttpContext context, HealthReport result)
{
context.Response.ContentType = "application/json";
var json = new JObject(
new JProperty("status", result.Status.ToString()),
new JProperty("results", new JObject(result.Entries.Select(pair =>
new JProperty(pair.Key, new JObject(
new JProperty("status", pair.Value.Status.ToString()),
new JProperty("description", pair.Value.Description),
new JProperty("data", new JObject(pair.Value.Data.Select(
p => new JProperty(p.Key, p.Value))))))))));
return context.Response.WriteAsync(
json.ToString(Formatting.Indented));
}
In the sample app, comment out the SYSTEM_TEXT_JSON
preprocessor directive in CustomWriterStartup.cs
to enable the Newtonsoft.Json
version of WriteResponse
.
The health checks API doesn't provide built-in support for complex JSON return formats because the format is specific to your choice of monitoring system. Customize the response in the preceding examples as needed. For more information on JSON serialization with System.Text.Json
, see How to serialize and deserialize JSON in .NET.
Database probe
A health check can specify a database query to run as a boolean test to indicate if the database is responding normally.
The sample app uses AspNetCore.Diagnostics.HealthChecks
, a health check library for ASP.NET Core apps, to run a health check on a SQL Server database. AspNetCore.Diagnostics.HealthChecks
executes a SELECT 1
query against the database to confirm the connection to the database is healthy.
Warning
When checking a database connection with a query, choose a query that returns quickly. The query approach runs the risk of overloading the database and degrading its performance. In most cases, running a test query isn't necessary. Merely making a successful connection to the database is sufficient. If you find it necessary to run a query, choose a simple SELECT query, such as SELECT 1
.
Include a package reference to AspNetCore.HealthChecks.SqlServer
.
Supply a valid database connection string in the appsettings.json
file of the sample app. The app uses a SQL Server database named HealthCheckSample
:
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=HealthCheckSample;Trusted_Connection=True;MultipleActiveResultSets=true;ConnectRetryCount=0"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"Console": {
"IncludeScopes": "true"
}
},
"AllowedHosts": "*"
}
Register health check services with AddHealthChecks in Startup.ConfigureServices
. The sample app calls the AddSqlServer
method with the database's connection string (DbHealthStartup.cs
):
services.AddHealthChecks()
.AddSqlServer(Configuration["ConnectionStrings:DefaultConnection"]);
A health check endpoint is created by calling MapHealthChecks
in Startup.Configure
:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health");
}
To run the database probe scenario using the sample app, execute the following command from the project's folder in a command shell:
dotnet run --scenario db
Note
AspNetCore.Diagnostics.HealthChecks
isn't maintained or supported by Microsoft.
Entity Framework Core DbContext probe
The DbContext
check confirms that the app can communicate with the database configured for an EF Core DbContext
. The DbContext
check is supported in apps that:
- Use Entity Framework (EF) Core.
- Include a package reference to
Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore
.
AddDbContextCheck<TContext>
registers a health check for a DbContext
. The DbContext
is supplied as the TContext
to the method. An overload is available to configure the failure status, tags, and a custom test query.
By default:
- The
DbContextHealthCheck
calls EF Core'sCanConnectAsync
method. You can customize what operation is run when checking health usingAddDbContextCheck
method overloads. - The name of the health check is the name of the
TContext
type.
In the sample app, AppDbContext
is provided to AddDbContextCheck
and registered as a service in Startup.ConfigureServices
(DbContextHealthStartup.cs
):
services.AddHealthChecks()
.AddDbContextCheck<AppDbContext>();
services.AddDbContext<AppDbContext>(options =>
{
options.UseSqlServer(
Configuration["ConnectionStrings:DefaultConnection"]);
});
A health check endpoint is created by calling MapHealthChecks
in Startup.Configure
:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health");
}
To run the DbContext
probe scenario using the sample app, confirm that the database specified by the connection string doesn't exist in the SQL Server instance. If the database exists, delete it.
Execute the following command from the project's folder in a command shell:
dotnet run --scenario dbcontext
After the app is running, check the health status by making a request to the /health
endpoint in a browser. The database and AppDbContext
don't exist, so app provides the following response:
Unhealthy
Trigger the sample app to create the database. Make a request to /createdatabase
. The app responds:
Creating the database...
Done!
Navigate to /health to see the health status.
Make a request to the /health
endpoint. The database and context exist, so app responds:
Healthy
Trigger the sample app to delete the database. Make a request to /deletedatabase
. The app responds:
Deleting the database...
Done!
Navigate to /health to see the health status.
Make a request to the /health
endpoint. The app provides an unhealthy response:
Unhealthy
Separate readiness and liveness probes
In some hosting scenarios, a pair of health checks is used to distinguish two app states:
- Readiness indicates if the app is running normally but isn't ready to receive requests.
- Liveness indicates if an app has crashed and must be restarted.
Consider the following example: An app must download a large configuration file before it's ready to process requests. We don't want the app to be restarted if the initial download fails because the app can retry downloading the file several times. We use a liveness probe to describe the liveness of the process, no other checks are run. We also want to prevent requests from being sent to the app before the configuration file download has succeeded. We use a readiness probe to indicate a "not ready" state until the download succeeds and the app is ready to receive requests.
The sample app contains a health check to report the completion of long-running startup task in a Hosted Service. The StartupHostedServiceHealthCheck
exposes a property, StartupTaskCompleted
, that the hosted service can set to true
when its long-running task is finished (StartupHostedServiceHealthCheck.cs
):
public class StartupHostedServiceHealthCheck : IHealthCheck
{
private volatile bool _startupTaskCompleted = false;
public string Name => "slow_dependency_check";
public bool StartupTaskCompleted
{
get => _startupTaskCompleted;
set => _startupTaskCompleted = value;
}
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default(CancellationToken))
{
if (StartupTaskCompleted)
{
return Task.FromResult(
HealthCheckResult.Healthy("The startup task is finished."));
}
return Task.FromResult(
HealthCheckResult.Unhealthy("The startup task is still running."));
}
}
The long-running background task is started by a Hosted Service (Services/StartupHostedService
). At the conclusion of the task, StartupHostedServiceHealthCheck.StartupTaskCompleted
is set to true
:
public class StartupHostedService : IHostedService, IDisposable
{
private readonly int _delaySeconds = 15;
private readonly ILogger _logger;
private readonly StartupHostedServiceHealthCheck _startupHostedServiceHealthCheck;
public StartupHostedService(ILogger<StartupHostedService> logger,
StartupHostedServiceHealthCheck startupHostedServiceHealthCheck)
{
_logger = logger;
_startupHostedServiceHealthCheck = startupHostedServiceHealthCheck;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Startup Background Service is starting.");
// Simulate the effect of a long-running startup task.
Task.Run(async () =>
{
await Task.Delay(_delaySeconds * 1000);
_startupHostedServiceHealthCheck.StartupTaskCompleted = true;
_logger.LogInformation("Startup Background Service has started.");
});
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Startup Background Service is stopping.");
return Task.CompletedTask;
}
public void Dispose()
{
}
}
The health check is registered with AddCheck in Startup.ConfigureServices
along with the hosted service. Because the hosted service must set the property on the health check, the health check is also registered in the service container (LivenessProbeStartup.cs
):
services.AddHostedService<StartupHostedService>();
services.AddSingleton<StartupHostedServiceHealthCheck>();
services.AddHealthChecks()
.AddCheck<StartupHostedServiceHealthCheck>(
"hosted_service_startup",
failureStatus: HealthStatus.Degraded,
tags: new[] { "ready" });
services.Configure<HealthCheckPublisherOptions>(options =>
{
options.Delay = TimeSpan.FromSeconds(2);
options.Predicate = (check) => check.Tags.Contains("ready");
});
services.AddSingleton<IHealthCheckPublisher, ReadinessPublisher>();
A health check endpoint is created by calling MapHealthChecks
in Startup.Configure
. In the sample app, the health check endpoints are created at:
/health/ready
for the readiness check. The readiness check filters health checks to the health check with theready
tag./health/live
for the liveness check. The liveness check filters out theStartupHostedServiceHealthCheck
by returningfalse
in theHealthCheckOptions.Predicate
(for more information, see Filter health checks)
In the following example code:
- The readiness check uses all registered checks with the 'ready' tag.
- The
Predicate
excludes all checks and returns a 200-Ok.
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health/ready", new HealthCheckOptions()
{
Predicate = (check) => check.Tags.Contains("ready"),
});
endpoints.MapHealthChecks("/health/live", new HealthCheckOptions()
{
Predicate = (_) => false
});
}
To run the readiness/liveness configuration scenario using the sample app, execute the following command from the project's folder in a command shell:
dotnet run --scenario liveness
In a browser, visit /health/ready
several times until 15 seconds have passed. The health check reports Unhealthy
for the first 15 seconds. After 15 seconds, the endpoint reports Healthy
, which reflects the completion of the long-running task by the hosted service.
This example also creates a Health Check Publisher (IHealthCheckPublisher implementation) that runs the first readiness check with a two-second delay. For more information, see the Health Check Publisher section.
Kubernetes example
Using separate readiness and liveness checks is useful in an environment such as Kubernetes. In Kubernetes, an app might be required to run time-consuming startup work before accepting requests, such as a test of the underlying database availability. Using separate checks allows the orchestrator to distinguish whether the app is functioning but not yet ready or if the app has failed to start. For more information on readiness and liveness probes in Kubernetes, see Configure Liveness and Readiness Probes in the Kubernetes documentation.
The following example demonstrates a Kubernetes readiness probe configuration:
spec:
template:
spec:
readinessProbe:
# an http probe
httpGet:
path: /health/ready
port: 80
# length of time to wait for a pod to initialize
# after pod startup, before applying health checking
initialDelaySeconds: 30
timeoutSeconds: 1
ports:
- containerPort: 80
Metric-based probe with a custom response writer
The sample app demonstrates a memory health check with a custom response writer.
MemoryHealthCheck
reports a degraded status if the app uses more than a given threshold of memory (1 GB in the sample app). The HealthCheckResult includes Garbage Collector (GC) information for the app (MemoryHealthCheck.cs
):
public class MemoryHealthCheck : IHealthCheck
{
private readonly IOptionsMonitor<MemoryCheckOptions> _options;
public MemoryHealthCheck(IOptionsMonitor<MemoryCheckOptions> options)
{
_options = options;
}
public string Name => "memory_check";
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default(CancellationToken))
{
var options = _options.Get(context.Registration.Name);
// Include GC information in the reported diagnostics.
var allocated = GC.GetTotalMemory(forceFullCollection: false);
var data = new Dictionary<string, object>()
{
{ "AllocatedBytes", allocated },
{ "Gen0Collections", GC.CollectionCount(0) },
{ "Gen1Collections", GC.CollectionCount(1) },
{ "Gen2Collections", GC.CollectionCount(2) },
};
var status = (allocated < options.Threshold) ?
HealthStatus.Healthy : context.Registration.FailureStatus;
return Task.FromResult(new HealthCheckResult(
status,
description: "Reports degraded status if allocated bytes " +
$">= {options.Threshold} bytes.",
exception: null,
data: data));
}
}
Register health check services with AddHealthChecks in Startup.ConfigureServices
. Instead of enabling the health check by passing it to AddCheck, the MemoryHealthCheck
is registered as a service. All IHealthCheck registered services are available to the health check services and middleware. We recommend registering health check services as Singleton services.
In CustomWriterStartup.cs
of the sample app:
services.AddHealthChecks()
.AddMemoryHealthCheck("memory");
A health check endpoint is created by calling MapHealthChecks
in Startup.Configure
. A WriteResponse
delegate is provided to the ResponseWriter property to output a custom JSON response when the health check executes:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{
ResponseWriter = WriteResponse
});
}
The WriteResponse
delegate formats the CompositeHealthCheckResult
into a JSON object and yields JSON output for the health check response. For more information, see the Customize output section.
To run the metric-based probe with custom response writer output using the sample app, execute the following command from the project's folder in a command shell:
dotnet run --scenario writer
Note
AspNetCore.Diagnostics.HealthChecks
includes metric-based health check scenarios, including disk storage and maximum value liveness checks.
AspNetCore.Diagnostics.HealthChecks
isn't maintained or supported by Microsoft.
Filter by port
Call RequireHost
on MapHealthChecks
with a URL pattern that specifies a port to restrict health check requests to the port specified. This approach is typically used in a container environment to expose a port for monitoring services.
The sample app configures the port using the Environment Variable Configuration Provider. The port is set in the launchSettings.json
file and passed to the configuration provider via an environment variable. You must also configure the server to listen to requests on the management port.
To use the sample app to demonstrate management port configuration, create the launchSettings.json
file in a Properties
folder.
The following Properties/launchSettings.json
file in the sample app isn't included in the sample app's project files and must be created manually:
{
"profiles": {
"SampleApp": {
"commandName": "Project",
"commandLineArgs": "",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_URLS": "http://localhost:5000/;http://localhost:5001/",
"ASPNETCORE_MANAGEMENTPORT": "5001"
},
"applicationUrl": "http://localhost:5000/"
}
}
}
Register health check services with AddHealthChecks in Startup.ConfigureServices
. Create a health check endpoint by calling MapHealthChecks
in Startup.Configure
.
In the sample app, a call to RequireHost
on the endpoint in Startup.Configure
specifies the management port from configuration:
endpoints.MapHealthChecks("/health")
.RequireHost($"*:{Configuration["ManagementPort"]}");
Endpoints are created in the sample app in Startup.Configure
. In the following example code:
- The readiness check uses all registered checks with the 'ready' tag.
- The
Predicate
excludes all checks and returns a 200-Ok.
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health/ready", new HealthCheckOptions()
{
Predicate = (check) => check.Tags.Contains("ready"),
});
endpoints.MapHealthChecks("/health/live", new HealthCheckOptions()
{
Predicate = (_) => false
});
}
Note
You can avoid creating the launchSettings.json
file in the sample app by setting the management port explicitly in code. In Program.cs
where the HostBuilder is created, add a call to ListenAnyIP and provide the app's management port endpoint. In Configure
of ManagementPortStartup.cs
, specify the management port with RequireHost
:
Program.cs
:
return new HostBuilder()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseKestrel()
.ConfigureKestrel(serverOptions =>
{
serverOptions.ListenAnyIP(5001);
})
.UseStartup(startupType);
})
.Build();
ManagementPortStartup.cs
:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health").RequireHost("*:5001");
});
To run the management port configuration scenario using the sample app, execute the following command from the project's folder in a command shell:
dotnet run --scenario port
Distribute a health check library
To distribute a health check as a library:
Write a health check that implements the IHealthCheck interface as a standalone class. The class can rely on dependency injection (DI), type activation, and named options to access configuration data.
In the health checks logic of
CheckHealthAsync
:data1
anddata2
are used in the method to run the probe's health check logic.AccessViolationException
is handled.
When an AccessViolationException occurs, the FailureStatus is returned with the HealthCheckResult to allow users to configure the health checks failure status.
using System; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Diagnostics.HealthChecks; namespace SampleApp { public class ExampleHealthCheck : IHealthCheck { private readonly string _data1; private readonly int? _data2; public ExampleHealthCheck(string data1, int? data2) { _data1 = data1 ?? throw new ArgumentNullException(nameof(data1)); _data2 = data2 ?? throw new ArgumentNullException(nameof(data2)); } public async Task<HealthCheckResult> CheckHealthAsync( HealthCheckContext context, CancellationToken cancellationToken) { try { return HealthCheckResult.Healthy(); } catch (AccessViolationException ex) { return new HealthCheckResult( context.Registration.FailureStatus, description: "An access violation occurred during the check.", exception: ex, data: null); } } } }
Write an extension method with parameters that the consuming app calls in its
Startup.Configure
method. In the following example, assume the following health check method signature:ExampleHealthCheck(string, string, int )
The preceding signature indicates that the
ExampleHealthCheck
requires additional data to process the health check probe logic. The data is provided to the delegate used to create the health check instance when the health check is registered with an extension method. In the following example, the caller specifies optional:- health check name (
name
). Ifnull
,example_health_check
is used. - string data point for the health check (
data1
). - integer data point for the health check (
data2
). Ifnull
,1
is used. - failure status (HealthStatus). The default is
null
. Ifnull
, HealthStatus.Unhealthy is reported for a failure status. - tags (
IEnumerable<string>
).
using System.Collections.Generic; using Microsoft.Extensions.Diagnostics.HealthChecks; public static class ExampleHealthCheckBuilderExtensions { const string DefaultName = "example_health_check"; public static IHealthChecksBuilder AddExampleHealthCheck( this IHealthChecksBuilder builder, string name = default, string data1, int data2 = 1, HealthStatus? failureStatus = default, IEnumerable<string> tags = default) { return builder.Add(new HealthCheckRegistration( name ?? DefaultName, sp => new ExampleHealthCheck(data1, data2), failureStatus, tags)); } }
- health check name (
Health Check Publisher
When an IHealthCheckPublisher is added to the service container, the health check system periodically executes your health checks and calls PublishAsync
with the result. This is useful in a push-based health monitoring system scenario that expects each process to call the monitoring system periodically in order to determine health.
The IHealthCheckPublisher interface has a single method:
Task PublishAsync(HealthReport report, CancellationToken cancellationToken);
HealthCheckPublisherOptions allow you to set:
- Delay: The initial delay applied after the app starts before executing IHealthCheckPublisher instances. The delay is applied once at startup and doesn't apply to subsequent iterations. The default value is five seconds.
- Period: The period of IHealthCheckPublisher execution. The default value is 30 seconds.
- Predicate: If Predicate is
null
(default), the health check publisher service runs all registered health checks. To run a subset of health checks, provide a function that filters the set of checks. The predicate is evaluated each period. - Timeout: The timeout for executing the health checks for all IHealthCheckPublisher instances. Use InfiniteTimeSpan to execute without a timeout. The default value is 30 seconds.
In the sample app, ReadinessPublisher
is an IHealthCheckPublisher implementation. The health check status is logged for each check at a log level of:
- Information (LogInformation) if the health checks status is Healthy.
- Error (LogError) if the status is either Degraded or Unhealthy.
public class ReadinessPublisher : IHealthCheckPublisher
{
private readonly ILogger _logger;
public ReadinessPublisher(ILogger<ReadinessPublisher> logger)
{
_logger = logger;
}
// The following example is for demonstration purposes only. Health Checks
// Middleware already logs health checks results. A real-world readiness
// check in a production app might perform a set of more expensive or
// time-consuming checks to determine if other resources are responding
// properly.
public Task PublishAsync(HealthReport report,
CancellationToken cancellationToken)
{
if (report.Status == HealthStatus.Healthy)
{
_logger.LogInformation("{Timestamp} Readiness Probe Status: {Result}",
DateTime.UtcNow, report.Status);
}
else
{
_logger.LogError("{Timestamp} Readiness Probe Status: {Result}",
DateTime.UtcNow, report.Status);
}
cancellationToken.ThrowIfCancellationRequested();
return Task.CompletedTask;
}
}
In the sample app's LivenessProbeStartup
example, the StartupHostedService
readiness check has a two second startup delay and runs the check every 30 seconds. To activate the IHealthCheckPublisher implementation, the sample registers ReadinessPublisher
as a singleton service in the dependency injection (DI) container:
services.AddHostedService<StartupHostedService>();
services.AddSingleton<StartupHostedServiceHealthCheck>();
services.AddHealthChecks()
.AddCheck<StartupHostedServiceHealthCheck>(
"hosted_service_startup",
failureStatus: HealthStatus.Degraded,
tags: new[] { "ready" });
services.Configure<HealthCheckPublisherOptions>(options =>
{
options.Delay = TimeSpan.FromSeconds(2);
options.Predicate = (check) => check.Tags.Contains("ready");
});
services.AddSingleton<IHealthCheckPublisher, ReadinessPublisher>();
Note
AspNetCore.Diagnostics.HealthChecks
includes publishers for several systems, including Application Insights.
AspNetCore.Diagnostics.HealthChecks
isn't maintained or supported by Microsoft.
Restrict health checks with MapWhen
Use MapWhen to conditionally branch the request pipeline for health check endpoints.
In the following example, MapWhen
branches the request pipeline to activate Health Checks Middleware if a GET request is received for the api/HealthCheck
endpoint:
app.MapWhen(
context => context.Request.Method == HttpMethod.Get.Method &&
context.Request.Path.StartsWith("/api/HealthCheck"),
builder => builder.UseHealthChecks());
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
For more information, see ASP.NET Core Middleware.
ASP.NET Core offers Health Checks Middleware and libraries for reporting the health of app infrastructure components.
Health checks are exposed by an app as HTTP endpoints. Health check endpoints can be configured for various real-time monitoring scenarios:
- Health probes can be used by container orchestrators and load balancers to check an app's status. For example, a container orchestrator may respond to a failing health check by halting a rolling deployment or restarting a container. A load balancer might react to an unhealthy app by routing traffic away from the failing instance to a healthy instance.
- Use of memory, disk, and other physical server resources can be monitored for healthy status.
- Health checks can test an app's dependencies, such as databases and external service endpoints, to confirm availability and normal functioning.
Health checks are typically used with an external monitoring service or container orchestrator to check the status of an app. Before adding health checks to an app, decide on which monitoring system to use. The monitoring system dictates what types of health checks to create and how to configure their endpoints.
Basic health probe
For many apps, a basic health probe configuration that reports the app's availability to process requests (liveness) is sufficient to discover the status of the app.
The basic configuration registers health check services and calls the Health Checks Middleware to respond at a URL endpoint with a health response. By default, no specific health checks are registered to test any particular dependency or subsystem. The app is considered healthy if it can respond at the health endpoint URL. The default response writer writes HealthStatus as a plaintext response to the client. The HealthStatus
is HealthStatus.Healthy, HealthStatus.Degraded, or HealthStatus.Unhealthy.
Register health check services with AddHealthChecks in Program.cs
. Create a health check endpoint by calling MapHealthChecks.
The following example creates a health check endpoint at /healthz
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks();
var app = builder.Build();
app.MapHealthChecks("/healthz");
app.Run();
Docker HEALTHCHECK
Docker offers a built-in HEALTHCHECK
directive that can be used to check the status of an app that uses the basic health check configuration:
HEALTHCHECK CMD curl --fail http://localhost:5000/healthz || exit
The preceding example uses curl
to make an HTTP request to the health check endpoint at /healthz
. curl
isn't included in the .NET Linux container images, but it can be added by installing the required package in the Dockerfile. Containers that use images based on Alpine Linux can use the included wget
in place of curl
.
Create health checks
Health checks are created by implementing the IHealthCheck interface. The CheckHealthAsync method returns a HealthCheckResult that indicates the health as Healthy
, Degraded
, or Unhealthy
. The result is written as a plaintext response with a configurable status code. Configuration is described in the Health check options section. HealthCheckResult can also return optional key-value pairs.
The following example demonstrates the layout of a health check:
public class SampleHealthCheck : IHealthCheck
{
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context, CancellationToken cancellationToken = default)
{
var isHealthy = true;
// ...
if (isHealthy)
{
return Task.FromResult(
HealthCheckResult.Healthy("A healthy result."));
}
return Task.FromResult(
new HealthCheckResult(
context.Registration.FailureStatus, "An unhealthy result."));
}
}
The health check's logic is placed in the CheckHealthAsync method. The preceding example sets a dummy variable, isHealthy
, to true
. If the value of isHealthy
is set to false
, the HealthCheckRegistration.FailureStatus status is returned.
If CheckHealthAsync throws an exception during the check, a new HealthReportEntry is returned with its HealthReportEntry.Status set to the FailureStatus. This status is defined by AddCheck (see the Register health check services section) and includes the inner exception that caused the check failure. The Description is set to the exception's message.
Register health check services
To register a health check service, call AddCheck in Program.cs
:
builder.Services.AddHealthChecks()
.AddCheck<SampleHealthCheck>("Sample");
The AddCheck overload shown in the following example sets the failure status (HealthStatus) to report when the health check reports a failure. If the failure status is set to null
(default), HealthStatus.Unhealthy is reported. This overload is a useful scenario for library authors, where the failure status indicated by the library is enforced by the app when a health check failure occurs if the health check implementation honors the setting.
Tags can be used to filter health checks. Tags are described in the Filter health checks section.
builder.Services.AddHealthChecks()
.AddCheck<SampleHealthCheck>(
"Sample",
failureStatus: HealthStatus.Degraded,
tags: new[] { "sample" });
AddCheck can also execute a lambda function. In the following example, the health check always returns a healthy result:
builder.Services.AddHealthChecks()
.AddCheck("Sample", () => HealthCheckResult.Healthy("A healthy result."));
Call AddTypeActivatedCheck to pass arguments to a health check implementation. In the following example, a type-activated health check accepts an integer and a string in its constructor:
public class SampleHealthCheckWithArgs : IHealthCheck
{
private readonly int _arg1;
private readonly string _arg2;
public SampleHealthCheckWithArgs(int arg1, string arg2)
=> (_arg1, _arg2) = (arg1, arg2);
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context, CancellationToken cancellationToken = default)
{
// ...
return Task.FromResult(HealthCheckResult.Healthy("A healthy result."));
}
}
To register the preceding health check, call AddTypeActivatedCheck
with the integer and string passed as arguments:
builder.Services.AddHealthChecks()
.AddTypeActivatedCheck<SampleHealthCheckWithArgs>(
"Sample",
failureStatus: HealthStatus.Degraded,
tags: new[] { "sample" },
args: new object[] { 1, "Arg" });
Use Health Checks Routing
In Program.cs
, call MapHealthChecks
on the endpoint builder with the endpoint URL or relative path:
app.MapHealthChecks("/healthz");
Require host
Call RequireHost
to specify one or more permitted hosts for the health check endpoint. Hosts should be Unicode rather than punycode and may include a port. If a collection isn't supplied, any host is accepted:
app.MapHealthChecks("/healthz")
.RequireHost("www.contoso.com:5001");
To restrict the health check endpoint to respond only on a specific port, specify a port in the call to RequireHost
. This approach is typically used in a container environment to expose a port for monitoring services:
app.MapHealthChecks("/healthz")
.RequireHost("*:5001");
Warning
API that relies on the Host header, such as HttpRequest.Host and RequireHost, are subject to potential spoofing by clients.
To prevent host and port spoofing, use one of the following approaches:
- Use HttpContext.Connection (ConnectionInfo.LocalPort) where the ports are checked.
- Employ Host filtering.
To prevent unauthorized clients from spoofing the port, call RequireAuthorization:
app.MapHealthChecks("/healthz")
.RequireHost("*:5001")
.RequireAuthorization();
For more information, see Host matching in routes with RequireHost.
Require authorization
Call RequireAuthorization to run Authorization Middleware on the health check request endpoint. A RequireAuthorization
overload accepts one or more authorization policies. If a policy isn't provided, the default authorization policy is used:
app.MapHealthChecks("/healthz")
.RequireAuthorization();
Enable Cross-Origin Requests (CORS)
Although running health checks manually from a browser isn't a common scenario, CORS Middleware can be enabled by calling RequireCors
on the health checks endpoints. The RequireCors
overload accepts a CORS policy builder delegate (CorsPolicyBuilder
) or a policy name. For more information, see Enable Cross-Origin Requests (CORS) in ASP.NET Core.
Health check options
HealthCheckOptions provide an opportunity to customize health check behavior:
Filter health checks
By default, the Health Checks Middleware runs all registered health checks. To run a subset of health checks, provide a function that returns a boolean to the Predicate option.
The following example filters the health checks so that only those tagged with sample
run:
app.MapHealthChecks("/healthz", new HealthCheckOptions
{
Predicate = healthCheck => healthCheck.Tags.Contains("sample")
});
Customize the HTTP status code
Use ResultStatusCodes to customize the mapping of health status to HTTP status codes. The following StatusCodes assignments are the default values used by the middleware. Change the status code values to meet your requirements:
app.MapHealthChecks("/healthz", new HealthCheckOptions
{
ResultStatusCodes =
{
[HealthStatus.Healthy] = StatusCodes.Status200OK,
[HealthStatus.Degraded] = StatusCodes.Status200OK,
[HealthStatus.Unhealthy] = StatusCodes.Status503ServiceUnavailable
}
});
Suppress cache headers
AllowCachingResponses controls whether the Health Checks Middleware adds HTTP headers to a probe response to prevent response caching. If the value is false
(default), the middleware sets or overrides the Cache-Control
, Expires
, and Pragma
headers to prevent response caching. If the value is true
, the middleware doesn't modify the cache headers of the response:
app.MapHealthChecks("/healthz", new HealthCheckOptions
{
AllowCachingResponses = true
});
Customize output
To customize the output of a health checks report, set the HealthCheckOptions.ResponseWriter property to a delegate that writes the response:
app.MapHealthChecks("/healthz", new HealthCheckOptions
{
ResponseWriter = WriteResponse
});
The default delegate writes a minimal plaintext response with the string value of HealthReport.Status
. The following custom delegate outputs a custom JSON response using System.Text.Json:
private static Task WriteResponse(HttpContext context, HealthReport healthReport)
{
context.Response.ContentType = "application/json; charset=utf-8";
var options = new JsonWriterOptions { Indented = true };
using var memoryStream = new MemoryStream();
using (var jsonWriter = new Utf8JsonWriter(memoryStream, options))
{
jsonWriter.WriteStartObject();
jsonWriter.WriteString("status", healthReport.Status.ToString());
jsonWriter.WriteStartObject("results");
foreach (var healthReportEntry in healthReport.Entries)
{
jsonWriter.WriteStartObject(healthReportEntry.Key);
jsonWriter.WriteString("status",
healthReportEntry.Value.Status.ToString());
jsonWriter.WriteString("description",
healthReportEntry.Value.Description);
jsonWriter.WriteStartObject("data");
foreach (var item in healthReportEntry.Value.Data)
{
jsonWriter.WritePropertyName(item.Key);
JsonSerializer.Serialize(jsonWriter, item.Value,
item.Value?.GetType() ?? typeof(object));
}
jsonWriter.WriteEndObject();
jsonWriter.WriteEndObject();
}
jsonWriter.WriteEndObject();
jsonWriter.WriteEndObject();
}
return context.Response.WriteAsync(
Encoding.UTF8.GetString(memoryStream.ToArray()));
}
The health checks API doesn't provide built-in support for complex JSON return formats because the format is specific to your choice of monitoring system. Customize the response in the preceding examples as needed. For more information on JSON serialization with System.Text.Json
, see How to serialize and deserialize JSON in .NET.
Database probe
A health check can specify a database query to run as a boolean test to indicate if the database is responding normally.
AspNetCore.Diagnostics.HealthChecks
, a health check library for ASP.NET Core apps, includes a health check that runs against a SQL Server database. AspNetCore.Diagnostics.HealthChecks
executes a SELECT 1
query against the database to confirm the connection to the database is healthy.
Warning
When checking a database connection with a query, choose a query that returns quickly. The query approach runs the risk of overloading the database and degrading its performance. In most cases, running a test query isn't necessary. Merely making a successful connection to the database is sufficient. If you find it necessary to run a query, choose a simple SELECT query, such as SELECT 1
.
To use this SQL Server health check, include a package reference to the AspNetCore.HealthChecks.SqlServer
NuGet package. The following example registers the SQL Server health check:
builder.Services.AddHealthChecks()
.AddSqlServer(
builder.Configuration.GetConnectionString("DefaultConnection"));
Note
AspNetCore.Diagnostics.HealthChecks
isn't maintained or supported by Microsoft.
Entity Framework Core DbContext probe
The DbContext
check confirms that the app can communicate with the database configured for an EF Core DbContext
. The DbContext
check is supported in apps that:
- Use Entity Framework (EF) Core.
- Include a package reference to the
Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore
NuGet package.
AddDbContextCheck registers a health check for a DbContext. The DbContext
is supplied to the method as the TContext
. An overload is available to configure the failure status, tags, and a custom test query.
By default:
- The
DbContextHealthCheck
calls EF Core'sCanConnectAsync
method. You can customize what operation is run when checking health usingAddDbContextCheck
method overloads. - The name of the health check is the name of the
TContext
type.
The following example registers a DbContext
and an associated DbContextHealthCheck
:
builder.Services.AddDbContext<SampleDbContext>(options =>
options.UseSqlServer(
builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddHealthChecks()
.AddDbContextCheck<SampleDbContext>();
Separate readiness and liveness probes
In some hosting scenarios, a pair of health checks is used to distinguish two app states:
- Readiness indicates if the app is running normally but isn't ready to receive requests.
- Liveness indicates if an app has crashed and must be restarted.
Consider the following example: An app must download a large configuration file before it's ready to process requests. We don't want the app to be restarted if the initial download fails because the app can retry downloading the file several times. We use a liveness probe to describe the liveness of the process, no other checks are run. We also want to prevent requests from being sent to the app before the configuration file download has succeeded. We use a readiness probe to indicate a "not ready" state until the download succeeds and the app is ready to receive requests.
The following background task simulates a startup process that takes roughly 15 seconds. Once it completes, the task sets the StartupHealthCheck.StartupCompleted
property to true:
public class StartupBackgroundService : BackgroundService
{
private readonly StartupHealthCheck _healthCheck;
public StartupBackgroundService(StartupHealthCheck healthCheck)
=> _healthCheck = healthCheck;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// Simulate the effect of a long-running task.
await Task.Delay(TimeSpan.FromSeconds(15), stoppingToken);
_healthCheck.StartupCompleted = true;
}
}
The StartupHealthCheck
reports the completion of the long-running startup task and exposes the StartupCompleted
property that gets set by the background service:
public class StartupHealthCheck : IHealthCheck
{
private volatile bool _isReady;
public bool StartupCompleted
{
get => _isReady;
set => _isReady = value;
}
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context, CancellationToken cancellationToken = default)
{
if (StartupCompleted)
{
return Task.FromResult(HealthCheckResult.Healthy("The startup task has completed."));
}
return Task.FromResult(HealthCheckResult.Unhealthy("That startup task is still running."));
}
}
The health check is registered with AddCheck in Program.cs
along with the hosted service. Because the hosted service must set the property on the health check, the health check is also registered in the service container as a singleton:
builder.Services.AddHostedService<StartupBackgroundService>();
builder.Services.AddSingleton<StartupHealthCheck>();
builder.Services.AddHealthChecks()
.AddCheck<StartupHealthCheck>(
"Startup",
tags: new[] { "ready" });
To create two different health check endpoints, call MapHealthChecks
twice:
app.MapHealthChecks("/healthz/ready", new HealthCheckOptions
{
Predicate = healthCheck => healthCheck.Tags.Contains("ready")
});
app.MapHealthChecks("/healthz/live", new HealthCheckOptions
{
Predicate = _ => false
});
The preceding example creates the following health check endpoints:
/healthz/ready
for the readiness check. The readiness check filters health checks to those tagged withready
./healthz/live
for the liveness check. The liveness check filters out all health checks by returningfalse
in the HealthCheckOptions.Predicate delegate. For more information on filtering health checks, see Filter health checks in this article.
Before the startup task completes, the /healthz/ready
endpoint reports an Unhealthy
status. Once the startup task completes, this endpoint reports a Healthy
status. The /healthz/live
endpoint excludes all checks and reports a Healthy
status for all calls.
Kubernetes example
Using separate readiness and liveness checks is useful in an environment such as Kubernetes. In Kubernetes, an app might be required to run time-consuming startup work before accepting requests, such as a test of the underlying database availability. Using separate checks allows the orchestrator to distinguish whether the app is functioning but not yet ready or if the app has failed to start. For more information on readiness and liveness probes in Kubernetes, see Configure Liveness and Readiness Probes in the Kubernetes documentation.
The following example demonstrates a Kubernetes readiness probe configuration:
spec:
template:
spec:
readinessProbe:
# an http probe
httpGet:
path: /healthz/ready
port: 80
# length of time to wait for a pod to initialize
# after pod startup, before applying health checking
initialDelaySeconds: 30
timeoutSeconds: 1
ports:
- containerPort: 80
Distribute a health check library
To distribute a health check as a library:
Write a health check that implements the IHealthCheck interface as a standalone class. The class can rely on dependency injection (DI), type activation, and named options to access configuration data.
Write an extension method with parameters that the consuming app calls in its
Program.cs
method. Consider the following example health check, which acceptsarg1
andarg2
as constructor parameters:public SampleHealthCheckWithArgs(int arg1, string arg2) => (_arg1, _arg2) = (arg1, arg2);
The preceding signature indicates that the health check requires custom data to process the health check probe logic. The data is provided to the delegate used to create the health check instance when the health check is registered with an extension method. In the following example, the caller specifies:
arg1
: An integer data point for the health check.arg2
: A string argument for the health check.name
: An optional health check name. Ifnull
, a default value is used.failureStatus
: An optional HealthStatus, which is reported for a failure status. Ifnull
, HealthStatus.Unhealthy is used.tags
: An optionalIEnumerable<string>
collection of tags.
public static class SampleHealthCheckBuilderExtensions { private const string DefaultName = "Sample"; public static IHealthChecksBuilder AddSampleHealthCheck( this IHealthChecksBuilder healthChecksBuilder, int arg1, string arg2, string? name = null, HealthStatus? failureStatus = null, IEnumerable<string>? tags = default) { return healthChecksBuilder.Add( new HealthCheckRegistration( name ?? DefaultName, _ => new SampleHealthCheckWithArgs(arg1, arg2), failureStatus, tags)); } }
Health Check Publisher
When an IHealthCheckPublisher is added to the service container, the health check system periodically executes your health checks and calls PublishAsync with the result. This process is useful in a push-based health monitoring system scenario that expects each process to call the monitoring system periodically to determine health.
HealthCheckPublisherOptions allow you to set the:
- Delay: The initial delay applied after the app starts before executing IHealthCheckPublisher instances. The delay is applied once at startup and doesn't apply to later iterations. The default value is five seconds.
- Period: The period of IHealthCheckPublisher execution. The default value is 30 seconds.
- Predicate: If Predicate is
null
(default), the health check publisher service runs all registered health checks. To run a subset of health checks, provide a function that filters the set of checks. The predicate is evaluated each period. - Timeout: The timeout for executing the health checks for all IHealthCheckPublisher instances. Use InfiniteTimeSpan to execute without a timeout. The default value is 30 seconds.
The following example demonstrates the layout of a health publisher:
public class SampleHealthCheckPublisher : IHealthCheckPublisher
{
public Task PublishAsync(HealthReport report, CancellationToken cancellationToken)
{
if (report.Status == HealthStatus.Healthy)
{
// ...
}
else
{
// ...
}
return Task.CompletedTask;
}
}
The HealthCheckPublisherOptions class provides properties for configuring the behavior of the health check publisher.
The following example registers a health check publisher as a singleton and configures HealthCheckPublisherOptions:
builder.Services.Configure<HealthCheckPublisherOptions>(options =>
{
options.Delay = TimeSpan.FromSeconds(2);
options.Predicate = healthCheck => healthCheck.Tags.Contains("sample");
});
builder.Services.AddSingleton<IHealthCheckPublisher, SampleHealthCheckPublisher>();
Note
AspNetCore.Diagnostics.HealthChecks
includes publishers for several systems, including Application Insights.
AspNetCore.Diagnostics.HealthChecks
isn't maintained or supported by Microsoft.
Dependency Injection and Health Checks
It's possible to use dependency injection to consume an instance of a specific Type
inside a Health Check class. Dependency injection can be useful to inject options or a global configuration to a Health Check. Using dependency injection is not a common scenario to configure Health Checks. Usually, each Health Check is quite specific to the actual test and is configured using IHealthChecksBuilder
extension methods.
The following example shows a sample Health Check that retrieves a configuration object via dependency injection:
public class SampleHealthCheckWithDI : IHealthCheck
{
private readonly SampleHealthCheckWithDiConfig _config;
public SampleHealthCheckWithDI(SampleHealthCheckWithDiConfig config)
=> _config = config;
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context, CancellationToken cancellationToken = default)
{
var isHealthy = true;
// use _config ...
if (isHealthy)
{
return Task.FromResult(
HealthCheckResult.Healthy("A healthy result."));
}
return Task.FromResult(
new HealthCheckResult(
context.Registration.FailureStatus, "An unhealthy result."));
}
}
The SampleHealthCheckWithDiConfig
and the Health check needs to be added to the service container :
builder.Services.AddSingleton<SampleHealthCheckWithDiConfig>(new SampleHealthCheckWithDiConfig
{
BaseUriToCheck = new Uri("https://sample.contoso.com/api/")
});
builder.Services.AddHealthChecks()
.AddCheck<SampleHealthCheckWithDI>(
"With Dependency Injection",
tags: new[] { "inject" });
UseHealthChecks vs. MapHealthChecks
There are two ways to make health checks accessible to callers:
- UseHealthChecks registers middleware for handling health checks requests in the middleware pipeline.
- MapHealthChecks registers a health checks endpoint. The endpoint is matched and executed along with other endpoints in the app.
The advantage of using MapHealthChecks
over UseHealthChecks
is the ability to use endpoint aware middleware, such as authorization, and to have greater fine-grained control over the matching policy. The primary advantage of using UseHealthChecks
over MapHealthChecks
is controlling exactly where health checks runs in the middleware pipeline.
- Terminates the pipeline when a request matches the health check endpoint. Short-circuiting is often desirable because it avoids unnecessary work, such as logging and other middleware.
- Is primarily used for configuring the health check middleware in the pipeline.
- Can match any path on a port with a
null
or emptyPathString
. Allows performing a health check on any request made to the specified port. - Source code
MapHealthChecks allows:
- Mapping specific routes or endpoints for health checks.
- Customization of the URL or path where the health check endpoint is accessible.
- Mapping multiple health check endpoints with different routes or configurations. Multiple endpoint support:
- Enables separate endpoints for different types of health checks or components.
- Is used to differentiate between different aspects of the app's health or apply specific configurations to subsets of health checks.
- Source code
Additional resources
Note
This article was partially created with the help of artificial intelligence. Before publishing, an author reviewed and revised the content as needed. See Our principles for using AI-generated content in Microsoft Learn.
ASP.NET Core