ASP.NET Core health checks for Polly v8 circuit breakers — expose circuit-breaker state as /health endpoint responses so Kubernetes probes, load balancers, and monitoring dashboards can automatically react to your resilience state.
var stateProvider = new CircuitBreakerStateProvider();
services.AddResiliencePipeline("payments-api", builder =>
builder.AddCircuitBreaker(new CircuitBreakerStrategyOptions
{
StateProvider = stateProvider,
FailureRatio = 0.5,
MinimumThroughput = 5,
BreakDuration = TimeSpan.FromSeconds(30),
}));
services.AddHealthChecks()
.AddPollyCircuitBreaker("payments-api", stateProvider); // ← one lineWhen the circuit opens, /health returns Unhealthy — Kubernetes stops routing traffic, zero manual intervention required.
"How do I expose my circuit breaker state in the ASP.NET Core health endpoint?" is one of the most-asked Polly questions. Without this package you must write your own IHealthCheck, wire up CircuitBreakerStateProvider, and map the four circuit states manually. PollyHealthChecks does all of that in a single method call.
| Without PollyHealthChecks | With PollyHealthChecks |
|---|---|
Write a custom IHealthCheck per circuit |
One AddPollyCircuitBreaker() call |
| Manually map all 4 circuit states | Built-in Closed→Healthy, HalfOpen→Degraded, Open→Unhealthy |
| Re-implement for every microservice | Shared package, consistent behaviour |
| Forget to update when you add circuits | Register alongside the pipeline |
dotnet add package PollyHealthChecksTargets net6.0, net8.0, and net9.0.
Dependencies: Polly.Core 8.*, Microsoft.Extensions.Diagnostics.HealthChecks 8.*
using Polly.CircuitBreaker;
using PollyHealthChecks;
var stateProvider = new CircuitBreakerStateProvider();
services.AddResiliencePipeline("downstream-api", builder =>
builder.AddCircuitBreaker(new CircuitBreakerStrategyOptions
{
StateProvider = stateProvider,
FailureRatio = 0.5,
SamplingDuration = TimeSpan.FromSeconds(10),
MinimumThroughput = 5,
BreakDuration = TimeSpan.FromSeconds(30),
}));services.AddHealthChecks()
.AddPollyCircuitBreaker("downstream-api", stateProvider);app.MapHealthChecks("/health");| Circuit state | Health status | Meaning |
|---|---|---|
Closed |
Healthy |
Normal operation |
HalfOpen |
Degraded |
Testing recovery — partial traffic |
Open |
Unhealthy (configurable) |
Calls rejected — dependency down |
Isolated |
Unhealthy (configurable) |
Manually isolated |
Use tags to split circuit breaker health into separate liveness and readiness probes:
services.AddHealthChecks()
.AddPollyCircuitBreaker("payments-api", paymentsStateProvider, tags: ["ready"])
.AddPollyCircuitBreaker("inventory-api", inventoryStateProvider, tags: ["ready"])
.AddPollyCircuitBreaker("auth-api", authStateProvider, tags: ["live", "ready"]);
// Liveness — just the critical auth circuit
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
Predicate = r => r.Tags.Contains("live"),
});
// Readiness — all dependency circuits
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = r => r.Tags.Contains("ready"),
});Kubernetes deployment:
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5Monitor every downstream dependency independently:
services.AddHealthChecks()
.AddPollyCircuitBreaker("payments-api", paymentsStateProvider)
.AddPollyCircuitBreaker("inventory-api", inventoryStateProvider, failureStatus: HealthStatus.Degraded)
.AddPollyCircuitBreaker("auth-api", authStateProvider, tags: ["ready", "live"])
.AddPollyCircuitBreaker("email-service", emailStateProvider, failureStatus: HealthStatus.Degraded);Demote a non-critical circuit to Degraded so a single open circuit doesn't fail the entire readiness check:
services.AddHealthChecks()
// Critical — Unhealthy when open (default)
.AddPollyCircuitBreaker("payments-api", paymentsStateProvider)
// Non-critical — Degraded when open (app still serves traffic)
.AddPollyCircuitBreaker("analytics-api", analyticsStateProvider,
failureStatus: HealthStatus.Degraded);Works out-of-the-box with AspNetCore.HealthChecks.UI:
services.AddHealthChecksUI(opts =>
opts.AddHealthCheckEndpoint("App", "/health"))
.AddInMemoryStorage();
services.AddHealthChecks()
.AddPollyCircuitBreaker("payments-api", paymentsStateProvider)
.AddPollyCircuitBreaker("inventory-api", inventoryStateProvider);
app.MapHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse,
});
app.MapHealthChecksUI();var builder = WebApplication.CreateBuilder(args);
var paymentsStateProvider = new CircuitBreakerStateProvider();
var inventoryStateProvider = new CircuitBreakerStateProvider();
builder.Services.AddResiliencePipeline("payments-api", b =>
b.AddCircuitBreaker(new CircuitBreakerStrategyOptions
{
StateProvider = paymentsStateProvider,
FailureRatio = 0.5,
MinimumThroughput = 5,
BreakDuration = TimeSpan.FromSeconds(30),
}));
builder.Services.AddResiliencePipeline("inventory-api", b =>
b.AddCircuitBreaker(new CircuitBreakerStrategyOptions
{
StateProvider = inventoryStateProvider,
FailureRatio = 0.5,
MinimumThroughput = 5,
BreakDuration = TimeSpan.FromSeconds(30),
}));
builder.Services.AddHealthChecks()
.AddPollyCircuitBreaker("payments-api", paymentsStateProvider, tags: ["ready", "live"])
.AddPollyCircuitBreaker("inventory-api", inventoryStateProvider, tags: ["ready"]);
var app = builder.Build();
app.MapHealthChecks("/health/live", new HealthCheckOptions { Predicate = r => r.Tags.Contains("live") });
app.MapHealthChecks("/health/ready", new HealthCheckOptions { Predicate = r => r.Tags.Contains("ready") });
app.Run();| Package | Downloads | Description |
|---|---|---|
| PollyEFCore | Polly v8 resilience for EF Core queries and SaveChanges | |
| PollyOpenAI | Polly v8 resilience for OpenAI and Azure OpenAI — retry on 429, Retry-After, circuit breaker | |
| PollyRedis | Polly v8 resilience for StackExchange.Redis — retry, circuit breaker, timeout | |
| PollySignalR | Polly v8 exponential back-off reconnect policy for SignalR HubConnection | |
| PollyGrpc | Polly v8 resilience (retry, CB, timeout) for gRPC .NET clients via Interceptor | |
| PollyKafka | Polly v8 resilience (retry, CB, timeout) for Confluent.Kafka producers and consumers | |
| PollyAzureEventHub | Polly v8 for Azure Event Hubs | |
| PollyAzureServiceBus | Polly v8 resilience (retry, CB, timeout) for Azure Service Bus senders and receivers | |
| PollyMediatR | Polly v8 pipelines for MediatR request handlers | |
| PollyElasticsearch | Polly v8 for Elastic.Clients.Elasticsearch | |
| PollyAzureKeyVault | Polly v8 for Azure Key Vault | |
| PollySendGrid | Polly v8 for SendGrid | |
| PollyMassTransit | Polly v8 for MassTransit | |
| PollyAzureTableStorage | Polly v8 for Azure Table Storage | |
| PollyMailKit | MailKit SMTP email client | |
| PollyAzureQueueStorage | Azure Queue Storage QueueClient | |
| PollyHangfire | Hangfire IBackgroundJobClient | |
| PollyBackoff | Jitter, linear & custom backoff for Polly v8 retry | |
| PollyChaos | Fault & latency injection (Simmy for Polly v8) | |
| PollyCaching | Cache-aside resilience strategy for Polly v8 | |
| PollyBulkhead | Bulkhead / concurrency limiter for Polly v8 | |
| PollyOpenTelemetry | OpenTelemetry metrics & tracing for Polly v8 |
If PollyHealthChecks is useful in your Kubernetes or monitoring setup, consider supporting the project:
💼 Need .NET / cloud-native help? Visit solidqualitysolutions.com for consulting and architecture services.
| PollyRabbitMQ | Polly v8 resilience for RabbitMQ.Client channels |
MIT