Skip to content

Microsoft.Extensions.Hosting.BackgroundService

Overview

BackgroundService is an abstract base class in Microsoft.Extensions.Hosting used to create long-running, hosted background tasks in .NET applications.

It is commonly used in:

  • Worker services (Windows Services, Linux systemd)
  • APIs that need background jobs (emails, events, clean-up tasks)
  • Message consumers (Kafka, RabbitMQ, Azure Service Bus)
  • Scheduled tasks
  • Pipelines and event processing

It integrates fully with:

  • Dependency injection
  • Logging
  • IHost / IHostBuilder
  • Graceful shutdown via CancellationToken

Architectural Purpose

A BackgroundService gives you a continuous or looping workflow that runs asynchronously in the background of your application until it is stopped.

You implement:

protected abstract Task ExecuteAsync(CancellationToken stoppingToken);

This method runs once and should contain your background loop.

High-Level Architecture Diagram

flowchart TD
    A[Host Web/Worker] --> B[BackgroundService Instance]
    B -->|ExecuteAsync| C[Long-running Loop / Processing]
    C --> D[External Services: Queues, APIs, DB, Files]
    B --> E[CancellationToken for Shutdown]

Basic Example

Step 1: Create a BackgroundService

public class MyWorker : BackgroundService
{
    private readonly ILogger<MyWorker> _logger;

    public MyWorker(ILogger<MyWorker> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
            await Task.Delay(1000, stoppingToken);
        }
    }
}

Step 2: Register in Program.cs (Minimal Hosting)

var builder = Host.CreateApplicationBuilder(args);

builder.Services.AddHostedService<MyWorker>();

var app = builder.Build();
app.Run();

When to Use BackgroundService

  • Polling external APIs
  • Processing message queues (Kafka, RabbitMQ, Azure Service Bus)
  • Data ingestion pipelines
  • Scheduled tasks (cron-like behaviour)
  • Periodic cache warmers
  • Cleanup / maintenance tasks
  • Long-running workflows
  • CPU-heavy pure computation → use Task.Run + thread pools
  • Short-lived tasks (just trigger them directly)
  • Cross-process durable messaging (use Azure Functions, MassTransit, Hangfire, Quartz.NET, etc.)

Correct Pattern: Using an Async Loop

A good BackgroundService implementation should:

  • Use a while loop
  • Support cancellation
  • Use Task.Delay or await async operations
  • Avoid blocking threads
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    while (!stoppingToken.IsCancellationRequested)
    {
        await DoWorkAsync(stoppingToken);
        await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
    }
}

Proper Shutdown Handling

.NET stops services in this order:

  1. Sends cancellation to the stoppingToken
  2. Waits for ExecuteAsync to exit gracefully
  3. Disposes the service

Always ensure:

  • No infinite loops without cancellation checks
  • No Thread.Sleep (blocks shutdown)
  • No swallowed exceptions

Advanced Example — BackgroundService + Channels

Perfect for message pipelines (e.g., Kafka → Processor):

public class MessageProcessor : BackgroundService
{
    private readonly ChannelReader<Message> _reader;

    public MessageProcessor(Channel<Message> channel)
    {
        _reader = channel.Reader;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await foreach (var msg in _reader.ReadAllAsync(stoppingToken))
        {
            await ProcessAsync(msg);
        }
    }
}

Using BackgroundService with Timers (Cleaner Pattern)

.NET 6+ introduces PeriodicTimer:

var timer = new PeriodicTimer(TimeSpan.FromSeconds(10));

while (await timer.WaitForNextTickAsync(stoppingToken))
{
    await DoWorkAsync();
}

Advantages:

  • More reliable than Task.Delay
  • Built for periodic jobs
  • Supports cancellation cleanly

10. Using BackgroundService in ASP.NET Core

Examples:

  • Webhook processors
  • Email dispatchers
  • File archiving / cleanup jobs
  • Metrics collectors

APIs remain responsive while work happens in the background.

11. Alternatives to BackgroundService

BackgroundService vs IHostedService

BackgroundService implements IHostedService — it’s a convenience class.

Aspect IHostedService BackgroundService
Code Boilerplate Minimal
StartAsync/StopAsync Manual Automatic
Loop logic Manual Built into ExecuteAsync

Always prefer BackgroundService unless you need very custom start/stop behaviour.

BackgroundService vs Windows Service

Feature BackgroundService Windows Service
Cross-platform
DI support
Hosting model Inside .NET Host OS-native service
Use case Applications OS-level background services

You can host a BackgroundService AS a Windows Service using UseWindowsService().

BackgroundService vs Hangfire / Quartz.NET

If you need:

Feature BackgroundService Hangfire / Quartz
Cron scheduling Manual Built-in
Persistence ✔ if you write it ✔ out-of-the-box
Distributed workers No Yes
GUI dashboards No Yes

Use Hangfire/Quartz for distributed or scheduled jobs. Use BackgroundService for internal, in-memory, continuous processing.

Advantages & Disadvantages

Advantages

  • Simple to use
  • Built into .NET runtime
  • First-class async support
  • Graceful shutdown handling
  • Integrates with DI and logging
  • Cross-platform (Windows/Linux/macOS)
  • Ideal for background pipelines, queues, and periodic jobs

Disadvantages

  • No persistence — jobs are lost if the service crashes
  • No built-in scheduling (must implement manually)
  • Can be misused for CPU-heavy tasks
  • Not suitable for distributed workloads
  • Harder to test than normal services (needs cancellation orchestration)

Summary

BackgroundService is an abstract base class in .NET used to implement long-running, asynchronous background tasks integrated with the .NET hosting model.

You override ExecuteAsync, implement an asynchronous loop, handle cancellation, and register your service using DI. It supports graceful shutdown, works cross-platform, and is ideal for pipelines, message processing, cleanup tasks, and continuous workflows inside a .NET app.

Compared to IHostedService, it removes boilerplate. It is not designed for scheduled or durable tasks — for that, use Hangfire or Quartz.NET. In modern .NET (including .NET 10), BackgroundService remains the standard for internal background workers.

See Also

Further Reading