Introduction
In most real-world applications, you often need to run background jobs—like sending emails, cleaning old logs, processing files, or updating reports—without blocking user requests.
Many developers reach straight for libraries like Hangfire, Quartz.NET, or Azure Functions.
But what if you want something lightweight, dependency-free, and built right into ASP.NET Core?
The answer is:
✅ Hosted Services — a powerful built-in feature in ASP.NET Core to run background tasks efficiently.
This article will show you how to build background jobs without Hangfire, step-by-step.
1. What Are Background Jobs?
A background job is any process that runs behind the scenes — not directly triggered by a user and not part of the main request/response flow.
Common Examples
Sending order confirmation emails after checkout
Generating and emailing PDF reports
Cleaning up temporary files
Syncing data with third-party APIs at intervals
These tasks can take time, and you don't want them to slow down user requests.
That's where Hosted Services come in.
2. What Are Hosted Services in ASP.NET Core?
ASP.NET Core includes a background task framework out of the box using IHostedService.
You can think of it as a service that starts with your application and runs continuously in the background.
There are mainly two types:
IHostedService– Run logic at startup and shutdown.BackgroundService– A helper base class for continuous or scheduled jobs.
3. Architecture Overview
Below is a high-level diagram of how Hosted Services work in ASP.NET Core.
Flowchart: Background Job Architecture
+----------------------------------+
| ASP.NET Core Application |
| (Web API / MVC / Razor / Blazor) |
+-------------------+--------------+
|
v
+----------------------------------+
| Hosted Service (Background Task) |
| - Runs Independently |
| - Starts with Application |
| - Handles Long/Recurring Jobs |
+-------------------+--------------+
|
v
+----------------------------------+
| Services / Database / APIs |
| (Email, File System, Cleanup) |
+----------------------------------+4. Step-by-Step Implementation
Let's create a simple background job that runs every 5 minutes and cleans up old logs from the database.
Step 1: Create the Hosted Service Class
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
public class LogCleanupService : BackgroundService
{
private readonly ILogger<LogCleanupService> _logger;
private readonly IServiceProvider _serviceProvider;
public LogCleanupService(ILogger<LogCleanupService> logger, IServiceProvider serviceProvider)
{
_logger = logger;
_serviceProvider = serviceProvider;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Log Cleanup Service started.");
while (!stoppingToken.IsCancellationRequested)
{
await DoCleanupAsync();
await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken); // Runs every 5 minutes
}
_logger.LogInformation("Log Cleanup Service stopped.");
}
private async Task DoCleanupAsync()
{
try
{
using (var scope = _serviceProvider.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<MyAppDbContext>();
var oldLogs = dbContext.Logs
.Where(l => l.CreatedDate < DateTime.UtcNow.AddDays(-30))
.ToList();
dbContext.Logs.RemoveRange(oldLogs);
await dbContext.SaveChangesAsync();
_logger.LogInformation($"{oldLogs.Count} old logs cleaned up successfully.");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while cleaning up logs.");
}
}
}

Join the conversation! Your thoughts help the community grow.