Modern ASP.NET Core applications rarely run as long-lived processes on a single server. Instead, they are hosted in Kubernetes, Docker containers, Azure App Service, virtual machines, and cloud environments where instances are started, stopped, scaled, and replaced continuously.
When an application is terminated abruptly, in-flight requests may fail, background jobs can be interrupted, messages may remain unprocessed, and data inconsistencies can occur. A graceful shutdown allows an application to stop accepting new work while safely completing ongoing operations before exiting.
ASP.NET Core provides built-in support for graceful shutdown through the Generic Host, cancellation tokens, and hosted services. When implemented correctly, graceful shutdown improves application reliability during deployments, scaling operations, and infrastructure maintenance.
In this article, you'll learn how graceful shutdown works in ASP.NET Core 10, how to respond to shutdown signals, safely stop background services, and follow production best practices.
Why Graceful Shutdown Matters
The Problem with Abrupt Termination
Consider a typical order processing application.
Customer
│
▼
ASP.NET Core API
│
▼
Order Service
│
┌──┴──────────────┐
▼ ▼
SQL Database Message Queue
If the application stops immediately:
Active HTTP requests fail.
Database transactions may be interrupted.
Messages may remain unprocessed.
Users receive unexpected errors.
Deployment reliability decreases.
A graceful shutdown minimizes these risks.
How Graceful Shutdown Works
When ASP.NET Core receives a termination signal:
The application stops accepting new requests.
Existing requests continue processing.
Background services receive a cancellation signal.
Cleanup operations execute.
The application exits after all work completes or the shutdown timeout expires.
This lifecycle allows applications to terminate safely instead of stopping immediately.
Responding to Application Shutdown
The host exposes the application lifetime through dependency injection.
public class StartupLogger
{
public StartupLogger(IHostApplicationLifetime lifetime,
ILogger<StartupLogger> logger)
{
lifetime.ApplicationStopping.Register(() =>
{
logger.LogInformation(
"Application is shutting down.");
});
}
}
Why Use ApplicationStopping?
The ApplicationStopping token allows services to begin cleanup as soon as shutdown starts.
This is useful for:
Closing external connections
Flushing logs
Completing transactions
Releasing unmanaged resources
Handling Cancellation in Background Services
Background services should always respect cancellation requests.
public class Worker : BackgroundService
{
protected override async Task ExecuteAsync(
CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await ProcessQueueAsync(stoppingToken);
await Task.Delay(
TimeSpan.FromSeconds(5),
stoppingToken);
}
}
}
Why Pass the Cancellation Token?
Ignoring the cancellation token prevents the application from shutting down promptly.
Passing the token to every asynchronous operation allows background processing to stop cleanly when shutdown begins.
Completing Outstanding Work
Some operations should finish before the application exits.
public async Task ProcessQueueAsync(
CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var message = await GetNextMessageAsync(
cancellationToken);
if (message is null)
return;
await HandleMessageAsync(
message,
cancellationToken);
}
}
Why Finish Current Work?
Completing the current message or request helps avoid:
Duplicate processing
Partial updates
Lost business events
Inconsistent application state
Exactly how much work should finish depends on your application's business requirements.
Graceful Shutdown in Containers
Container orchestrators such as Kubernetes send a termination signal before stopping a container.
Typical sequence:
Kubernetes
│
▼
SIGTERM
│
▼
ASP.NET Core
│
┌───┴──────────────┐
▼ ▼
Stop New Requests Finish Existing Work
│
▼
Application Stops
This allows rolling deployments without interrupting active users.
End-to-End Implementation
Consider an order processing platform deployed to Kubernetes.
Architecture:
Client
│
▼
Load Balancer
│
▼
ASP.NET Core API
│
┌─┴─────────────────────┐
▼ ▼
Background Worker SQL Database
│
▼
Message Queue
Workflow:
Join the conversation! Your thoughts help the community grow.