What it is
Hangfire runs background jobs in .NET with persistent storage, automatic retries, scheduling and a web dashboard — no separate service required.
Enqueue fire-and-forget jobs, schedule delayed ones, define recurring jobs with cron expressions, and chain continuations. State lives in the database.
Installation
dotnet add package HangfireGetting started
The smallest useful thing you can do with it, and what each part means.
csharp
// Fire and forget — runs once, as soon as a worker is free.
BackgroundJob.Enqueue<IEmailService>(x => x.SendWelcome(userId));
// Delayed.
BackgroundJob.Schedule<IEmailService>(x => x.SendReminder(userId), TimeSpan.FromDays(3));
// Recurring, by cron.
RecurringJob.AddOrUpdate<IReportService>(
"nightly-report", x => x.GenerateAsync(null!), "0 3 * * *");
// Continuation — runs only after the parent succeeds.
var id = BackgroundJob.Enqueue<IImportService>(x => x.Import(fileId));
BackgroundJob.ContinueJobWith<INotifyService>(id, x => x.Notify(fileId));Advanced usage
Where the library earns its place over a simpler alternative.
csharp
[AutomaticRetry(Attempts = 5, DelaysInSeconds = new[] { 60, 300, 900 })]
[DisableConcurrentExecution(timeoutInSeconds: 300)]
public async Task ProcessAsync(int orderId)
{
// The job must be idempotent: a retry may run it again after
// partial completion.
if (await _orders.IsProcessedAsync(orderId)) return;
await _orders.ProcessAsync(orderId);
}
// Secure the dashboard — it exposes job data and allows re-execution.
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[] { new AdminOnlyFilter() }
});Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- Job failed with a serialisation exception
- An argument cannot be serialised. Pass an id and load the object inside the job.
- Recurring jobs run on every instance
- That is expected — all servers poll the same storage. Hangfire coordinates, but use DisableConcurrentExecution for extra safety.
Best practices
- Write idempotent jobs; automatic retries mean a job can run more than once.
- Pass identifiers rather than objects — arguments are serialised into storage.
- Always secure the dashboard; it can trigger and inspect jobs.
- Use DisableConcurrentExecution for jobs that must not overlap across instances.
Background
Why it exists, and what it was reacting to.
Hangfire's distinguishing choice is storing jobs in your existing database, so a job survives an application restart without deploying Redis or a message broker.
