JobsMVCHangfireAugust 2026 · 8 min read

Background Jobs in ASP.NET Core MVC with Hangfire: Recurring and Fire-and-Forget

TL;DR

Hangfire brings reliable background jobs to ASP.NET Core MVC with a few lines: fire-and-forget for one-off work, delayed jobs, recurring cron jobs, and a built-in dashboard at /hangfire. Storage follows your database, so jobs survive app restarts.

Some work should not block a web request: sending emails, generating reports, syncing data, cleaning old logs. ASP.NET Core MVC apps solve this with Hangfire, an in-process job scheduler that persists jobs in your database and executes them reliably — even after a restart. This article sets up Hangfire in a .NET 10 MVC application and shows fire-and-forget, delayed, and recurring jobs with the built-in dashboard.

Setting Up Hangfire

Install the Hangfire and Hangfire.SqlServer packages, add Hangfire services, and map the dashboard:

builder.Services.AddHangfire(config =>
    config.UseSqlServerStorage(builder.Configuration.GetConnectionString("Default")));
builder.Services.AddHangfireServer();

var app = builder.Build();

app.UseHangfireDashboard("/hangfire");

Hangfire creates its own tables in the configured database, so jobs and their state survive application restarts. The dashboard at /hangfire shows queues, retries, and failed jobs in real time.

Fire-and-Forget Jobs

Enqueue one-off work from a controller. The job runs asynchronously on the Hangfire worker pool, and the HTTP request returns immediately:

public class InvoiceController : Controller
{
    public IActionResult Send(int id)
    {
        // runs in the background, request returns instantly
        BackgroundJob.Enqueue(() => _invoiceService.SendAsync(id));
        return RedirectToAction("Index");
    }
}

Only public methods on registered services should be called. Hangfire serializes the method call, so the target class must be resolved from DI — never call anonymous lambdas or private methods.

Delayed and Recurring Jobs

Delayed jobs run once after a timeout; recurring jobs run on a cron schedule:

// delayed: send a reminder in 1 hour
BackgroundJob.Schedule(() => _mailer.SendReminderAsync(id), TimeSpan.FromHours(1));

// recurring: run the log cleanup every day at 03:00
RecurringJob.AddOrUpdate<SerilogCleanupJob>(
    job => job.ExecuteAsync(CancellationToken.None),
    "0 3 * * *");

Recurring jobs are idempotent by design — the scheduler ensures they do not overlap, and the dashboard lets you trigger, pause, and delete schedules without redeploying.

Protecting the Dashboard

The dashboard exposes internals, so restrict it to admins in production:

app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
    Authorization = new[] { new HangfireAdminFilter() }
});
public class HangfireAdminFilter : IDashboardAuthorizationFilter
{
    public bool Authorize(DashboardContext context)
    {
        var http = context.GetHttpContext();
        return http.User?.IsInRole("Admin") == true;
    }
}

A Practical Sample: SerilogCleanupJob

Combine background jobs with logging. A recurring job that deletes logs older than three days keeps storage bounded without any manual maintenance — this exact job ships in the MVC EDevKit Basic starter:

public class SerilogCleanupJob
{
    public async Task ExecuteAsync(CancellationToken ct)
    {
        var cutoff = DateTime.UtcNow.AddDays(-3);
        var old = await _db.LogEntries.Where(l => l.CreatedAt < cutoff).ToListAsync(ct);
        if (old.Count == 0) return;
        _db.LogEntries.RemoveRange(old);
        await _db.SaveChangesAsync(ct);
    }
}

Key Takeaways

  • Hangfire persists jobs in your database, surviving restarts
  • Fire-and-forget, delayed, and recurring job types cover the common scheduling needs
  • The /hangfire dashboard gives you queues, retries, and failures at a glance
  • Protect the dashboard with an admin-only authorization filter
  • Pair recurring jobs with logging and cleanup to keep production storage bounded

FAQ

Does Hangfire work with SQL Server? Yes, SQL Server is the most widely used Hangfire storage, and PostgreSQL and Redis storage are also available.

Will jobs survive an application restart? Yes. Jobs and their state are persisted in the database, and Hangfire picks them up again when the server starts.

What happens if a job throws? Hangfire retries automatically with exponential backoff and marks the job as failed after the retry limit, where you can inspect and replay it from the dashboard.

Can recurring jobs overlap? Hangfire prevents overlapping executions of the same recurring job, so a slow run will not start a second instance.

Study Hangfire wired into a real MVC app?

MVC EDevKit Basic ships Hangfire with a /hangfire dashboard, recurring jobs, fire-and-forget, and a sample SerilogCleanupJob. $21.

View MVC EDevKit Details