Introduction
Modern cloud applications operate in distributed environments where failures are inevitable. APIs become temporarily unavailable, databases experience connectivity issues, external services respond slowly, and network interruptions can occur at any time. While these failures may be short-lived, they can significantly impact application reliability if not handled properly.
Many developers assume that cloud services are always available, but in reality, even the most reliable systems occasionally experience transient faults. A resilient application is designed to detect, handle, and recover from these failures gracefully rather than crashing or returning errors to users.
This is where Polly becomes valuable. Polly is a popular .NET resilience library that helps developers implement fault-handling policies such as retries, circuit breakers, timeouts, fallback mechanisms, and rate limiting. When combined with ASP.NET Core, Polly enables teams to build applications that remain reliable even when dependent services fail.
In this article, you'll learn how Polly works, explore common resilience patterns, and discover best practices for building fault-tolerant cloud applications with ASP.NET Core.
Why Resilience Matters in Cloud Applications
Consider a typical cloud-based application:
Web API
↓
External API
↓
Database
↓
Message Queue
Every dependency introduces a potential point of failure.
Common issues include:
Temporary network outages
DNS failures
Service throttling
API timeouts
Database connection interruptions
Infrastructure maintenance
Without resilience mechanisms, a single dependency failure can impact the entire application.
Instead of treating failures as exceptional events, cloud-native applications should assume failures will happen and be prepared to recover automatically.
What Is Polly?
Polly is an open-source .NET library designed for transient fault handling and resilience.
It provides reusable policies that allow applications to react intelligently to failures.
Common Polly policies include:
Retry
Circuit Breaker
Timeout
Fallback
Bulkhead Isolation
Rate Limiting
These policies can be applied to:
HTTP requests
Database operations
External service calls
Background jobs
Messaging systems
Polly integrates seamlessly with ASP.NET Core and HttpClient, making it a popular choice for cloud-native applications.
Installing Polly
Install the required package:
dotnet add package Microsoft.Extensions.Http.Polly
This package enables Polly integration with HttpClientFactory.
Implementing Retry Policies
Transient failures are one of the most common cloud application issues.
Examples include:
Temporary API outages
Short network interruptions
Service throttling
A retry policy automatically attempts the operation again before returning an error.
Example:
builder.Services.AddHttpClient("WeatherApi")
.AddTransientHttpErrorPolicy(policy =>
policy.WaitAndRetryAsync(
3,
retryAttempt =>
TimeSpan.FromSeconds(retryAttempt)));
In this example:
The request is retried up to three times.
Each retry waits longer than the previous one.
Temporary failures can often be resolved automatically.
Retry policies improve reliability without requiring user intervention.
Using Exponential Backoff
Retrying immediately can sometimes worsen the problem.
A better approach is exponential backoff.
builder.Services.AddHttpClient("OrdersApi")
.AddTransientHttpErrorPolicy(policy =>
policy.WaitAndRetryAsync(
3,
retryAttempt =>
TimeSpan.FromSeconds(
Math.Pow(2, retryAttempt))));
The delays become:
Retry 1 → 2 seconds
Retry 2 → 4 seconds
Retry 3 → 8 seconds
This reduces pressure on struggling services and improves recovery chances.
Implementing Circuit Breakers
Retries alone are not enough.
If a service remains unavailable, continuously retrying requests wastes resources.
Circuit breakers prevent repeated calls to failing services.
Example:
builder.Services.AddHttpClient("PaymentApi")
.AddTransientHttpErrorPolicy(policy =>
policy.CircuitBreakerAsync(
5,
TimeSpan.FromSeconds(30)));
In this configuration:
After five consecutive failures, the circuit opens.
Requests fail immediately for 30 seconds.
After the timeout, Polly tests whether the service has recovered.
The workflow looks like this:
Service Healthy
↓
Requests Allowed
↓
Repeated Failures
↓
Circuit Opens
↓
Requests Blocked
↓
Recovery Check
Circuit breakers protect both your application and the failing service.
Adding Timeout Policies
Some failures occur because services respond too slowly.
Without timeouts, requests may remain active indefinitely.
Example:
builder.Services.AddHttpClient("InventoryApi")
.AddPolicyHandler(
Policy.TimeoutAsync<HttpResponseMessage>(
TimeSpan.FromSeconds(5)));
If the request exceeds five seconds, Polly cancels the operation.
Benefits include:
Faster failure detection
Better resource utilization
Improved user experience
Timeouts are especially important for high-traffic APIs.
Implementing Fallback Policies
Sometimes an application can continue operating even when a dependency fails.
Fallback policies provide alternative behavior.
Example:
var fallbackPolicy =
Policy<string>
.Handle<Exception>()
.FallbackAsync(
"Service temporarily unavailable");
Instead of returning an exception, the application provides a controlled response.
Common fallback scenarios include:
Cached data
Default values
Maintenance messages
Alternative service providers
Fallbacks improve application availability during outages.
Combining Multiple Policies
The real power of Polly comes from combining policies.
Example:
builder.Services.AddHttpClient("ProductApi")
.AddTransientHttpErrorPolicy(policy =>
policy.WaitAndRetryAsync(3,
retry => TimeSpan.FromSeconds(retry)))
.AddTransientHttpErrorPolicy(policy =>
policy.CircuitBreakerAsync(
5,
TimeSpan.FromSeconds(30)));
The workflow becomes:
Request
↓
Retry Policy
↓
Circuit Breaker
↓
External Service
This layered approach provides stronger resilience than any individual policy.
Using Polly with ASP.NET Core APIs
Suppose an API retrieves product data from an external service.
public class ProductService
{
private readonly HttpClient _client;
public ProductService(HttpClient client)
{
_client = client;
}
public async Task<string> GetProductsAsync()
{
return await _client.GetStringAsync(
"/products");
}
}
Because Polly policies are configured through HttpClientFactory, the service automatically benefits from:
Retries
Circuit breakers
Timeouts
Fallbacks
No additional fault-handling code is required inside the service itself.
This keeps business logic clean and maintainable.
Common Resilience Patterns
| Pattern | Purpose |
|---|---|
| Retry | Recover from transient failures |
| Exponential Backoff | Reduce pressure during outages |
| Circuit Breaker | Prevent repeated failures |
| Timeout | Avoid long-running requests |
| Fallback | Provide alternative responses |
| Bulkhead Isolation | Limit failure impact |
| Rate Limiting | Control traffic volume |
Most production applications use several of these patterns together.
Best Practices
Retry Only Transient Failures
Do not retry every error.
For example:
Network timeouts → Retry
HTTP 500 errors → Retry
Invalid requests → Do not retry
Retrying permanent failures wastes resources.
Use Exponential Backoff
Avoid aggressive retry behavior.
Exponential backoff reduces load on struggling services.
Implement Circuit Breakers for Critical Dependencies
External services should always be protected with circuit breakers.
This prevents cascading failures.
Configure Appropriate Timeouts
Timeout values should reflect business requirements.
Very short timeouts may cause unnecessary failures.
Very long timeouts may reduce responsiveness.
Monitor Resilience Metrics
Track:
Retry counts
Circuit breaker activations
Timeout occurrences
Fallback executions
These metrics help identify system weaknesses.
Combine Polly with Observability
Use OpenTelemetry, Application Insights, or other monitoring tools to visualize resilience events.
Observability helps teams understand how policies behave in production.
Common Mistakes to Avoid
| Mistake | Impact |
|---|---|
| Retrying every exception | Increased resource consumption |
| Missing circuit breakers | Cascading failures |
| Excessive retry attempts | Service overload |
| No timeout policies | Resource exhaustion |
| Ignoring monitoring | Limited operational visibility |
| Hardcoded resilience settings | Difficult maintenance |
Avoiding these mistakes improves application stability and maintainability.
Conclusion
Building resilient cloud applications requires more than simply handling exceptions. Modern distributed systems must be designed to anticipate failures and recover gracefully when they occur. Polly provides a powerful and flexible way to implement resilience patterns such as retries, circuit breakers, timeouts, and fallback mechanisms within ASP.NET Core applications.
By integrating Polly with HttpClientFactory and following proven resilience practices, development teams can reduce downtime, improve reliability, and create better user experiences even when dependent services encounter problems. As cloud architectures continue to evolve, resilience is becoming a core requirement rather than an optional enhancement, making Polly an essential tool for every ASP.NET Core developer building production-ready applications.

Join the conversation! Your thoughts help the community grow.