ASP.NET Core  

Hangfire Cron Jobs Full Details Using ASP.NET Core

Modern applications often require background processing tasks such as sending emails, generating reports, or cleaning up data. Hangfire is a powerful library for background job processing in .NET applications. Its key features include reliable job execution, dashboard monitoring, and cron-based scheduling.

This article provides a deep dive into Hangfire cron jobs, including setup, advanced scheduling, best practices, and real-world considerations.

1. What is Hangfire?

Hangfire is an open-source library that allows you to run background jobs in .NET applications without writing a separate Windows Service or console app.

Key features

  • Persistent background jobs with SQL Server, Redis, or other storage.

  • Supports Fire-and-forget jobs, Delayed jobs, Recurring jobs, and Continuations.

  • Provides a Dashboard UI for monitoring jobs.

  • Scales with multiple servers in a distributed environment.

2. Hangfire Job Types

Job TypeDescription
Fire-and-ForgetExecutes once immediately after enqueueing.
DelayedExecutes once after a specific delay.
RecurringExecutes repeatedly based on a cron expression.
ContinuationsExecutes after a parent job completes.

This article focuses on Recurring Jobs using Cron.

3. Setting Up Hangfire in ASP.NET Core

3.1. Install NuGet Packages

dotnet add package Hangfire
dotnet add package Hangfire.AspNetCore
dotnet add package Hangfire.SqlServer

Hangfire supports multiple storage options, but SQL Server is most common in enterprise apps.

3.2. Configure Hangfire in Program.cs

using Hangfire;
using Hangfire.SqlServer;

var builder = WebApplication.CreateBuilder(args);

// Add Hangfire services
builder.Services.AddHangfire(config =>
{
    config.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
          .UseSimpleAssemblyNameTypeSerializer()
          .UseRecommendedSerializerSettings()
          .UseSqlServerStorage(builder.Configuration.GetConnectionString("HangfireConnection"), new SqlServerStorageOptions
          {
              CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
              SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
              QueuePollInterval = TimeSpan.Zero,
              UseRecommendedIsolationLevel = true,
              UsePageLocksOnDequeue = true,
              DisableGlobalLocks = true
          });
});

// Add Hangfire Server
builder.Services.AddHangfireServer();

var app = builder.Build();

// Configure Dashboard
app.UseHangfireDashboard("/hangfire");

// Example Fire-and-Forget Job
BackgroundJob.Enqueue(() => Console.WriteLine("Hello Hangfire!"));

app.Run();

3.3. Add Connection String

appsettings.json:

{"ConnectionStrings": {
    "HangfireConnection": "Server=localhost;Database=HangfireDB;Trusted_Connection=True;"}}

4. Understanding Cron Expressions

Hangfire uses Cron expressions to schedule recurring jobs. They follow this pattern:

┌───────────── minute (0 - 59)
│ ┌───────────── hour (0 - 23)
│ │ ┌───────────── day of month (1 - 31)
│ │ │ ┌───────────── month (1 - 12)
│ │ │ │ ┌───────────── day of week (0 - 6) (Sunday=0)
│ │ │ │ │
* * * * *

Hangfire provides a Cron class with pre-built schedules:

MethodCron ExpressionFrequency
Cron.Minutely()* * * * *Every minute
Cron.Hourly()0 * * * *Every hour
Cron.Daily()0 0 * * *Once daily at midnight
Cron.Weekly()0 0 * * 0Every Sunday midnight
Cron.Monthly()0 0 1 * *1st of every month
Cron.Yearly()0 0 1 1 *Every January 1st
Cron.Weekly(DayOfWeek.Monday)0 0 * * 1Every Monday midnight

You can also provide custom cron expressions like "30 14 * * 1-5" (Weekdays at 2:30 PM).

5. Creating Recurring Jobs

5.1. Simple Recurring Job

using Hangfire;

RecurringJob.AddOrUpdate(
    "daily-report",               // Job ID
    () => Console.WriteLine("Daily report generated."), // Method to call
    Cron.Daily);                  // Cron schedule
  • AddOrUpdate ensures the job is created or updated if it already exists.

  • Job IDs prevent duplicate recurring jobs.

5.2. Job With Parameters

public class ReportService
{
    public void GenerateReport(string reportType)
    {
        Console.WriteLine($"Generating {reportType} report at {DateTime.Now}");
    }
}

// Schedule
RecurringJob.AddOrUpdate<ReportService>(
    "sales-report",
    x => x.GenerateReport("Sales"),
    Cron.Daily);
  • Hangfire automatically serializes parameters.

  • Supports complex objects, but they must be serializable.

6. Using Cron Expressions for Advanced Scheduling

  • Every 15 minutes: "*/15 * * * *"Cron.MinuteInterval(15)

  • Weekdays at 9 AM: "0 9 * * 1-5" → Custom expression

  • Last day of the month at 11:59 PM: "59 23 L * *" → Hangfire supports advanced cron extensions using NCrontab.Advanced

Example

RecurringJob.AddOrUpdate(
    "quarterly-report",
    () => Console.WriteLine("Quarterly report generated"),
    "0 0 1 1,4,7,10 *"); // 1st of Jan, Apr, Jul, Oct at midnight

7. Using Hangfire Dashboard

Hangfire provides a built-in dashboard for monitoring jobs:

  • URL: /hangfire

  • Shows job statistics: succeeded, failed, processing, scheduled

  • Allows retrying failed jobs, deleting jobs, and triggering jobs manually

  • Supports authorization filters

Add authorization

app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
    Authorization = new[] { new MyAuthorizationFilter() }
});
using Hangfire.Dashboard;

public class MyAuthorizationFilter : IDashboardAuthorizationFilter
{
    public bool Authorize(DashboardContext context)
    {
        // Only allow local requests
        var httpContext = context.GetHttpContext();
        return httpContext.Request.IsLocal();
    }
}

8. Distributed Job Execution

Hangfire can scale across multiple servers:

  • Multiple ASP.NET Core instances can share the same SQL Server storage.

  • Jobs are locked per server to prevent duplicate execution.

  • Useful for cloud deployments (Azure App Service, Kubernetes, Docker).

Recommended settings for SQL Server storage

.UseSqlServerStorage(connectionString, new SqlServerStorageOptions
{
    QueuePollInterval = TimeSpan.FromSeconds(15),
    SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
    UseRecommendedIsolationLevel = true,
    DisableGlobalLocks = true
});

9. Handling Failures and Retries

  • Hangfire retries failed jobs automatically.

  • Default retry count: 10 times with exponential backoff.

  • Customize retry logic using AutomaticRetry attribute:

[AutomaticRetry(Attempts = 5, DelaysInSeconds = new int[] { 10, 60, 300 })]
public void SendEmail(string email)
{
    // send email logic
}
  • Failed jobs appear in Dashboard → Failed Jobs.

10. Logging and Monitoring

  • Use ILogger to track job execution.

  • Integrate Hangfire with Application Insights or Serilog for enterprise monitoring.

public class ReportService
{
    private readonly ILogger<ReportService> _logger;
    public ReportService(ILogger<ReportService> logger)
    {
        _logger = logger;
    }

    public void GenerateReport(string reportType)
    {
        _logger.LogInformation("Generating {ReportType} report at {Time}", reportType, DateTime.Now);
    }
}

11. Best Practices for Hangfire Cron Jobs

  1. Use AddOrUpdate to prevent duplicate recurring jobs.

  2. Assign unique job IDs for all recurring jobs.

  3. Keep jobs idempotent – safe to run multiple times without side effects.

  4. Avoid long-running jobs – break into smaller jobs or use batches.

  5. Secure the Dashboard with authentication/authorization.

  6. Monitor retries and failures to prevent silent failures.

  7. Prefer async methods for I/O-bound tasks.

  8. Use persistent storage (SQL Server or Redis) for production.

12. Example Real-World Scenario

Scenario: Send daily sales reports at 6 AM every day, weekly summary on Monday 8 AM, and monthly invoices on the 1st of each month.

RecurringJob.AddOrUpdate<ReportService>(
    "daily-sales",
    x => x.GenerateReport("Daily Sales"),
    Cron.Daily(6));

RecurringJob.AddOrUpdate<ReportService>(
    "weekly-summary",
    x => x.GenerateReport("Weekly Summary"),
    Cron.Weekly(DayOfWeek.Monday, 8));

RecurringJob.AddOrUpdate<InvoiceService>(
    "monthly-invoices",
    x => x.GenerateInvoices(),
    "0 0 1 * *"); // 1st day of each month at midnight
  • All jobs are monitored via Dashboard.

  • Failures are automatically retried.

  • Jobs can be triggered manually if needed.

Conclusion

Hangfire provides a robust, scalable, and developer-friendly way to manage background jobs in ASP.NET Core applications. With cron expressions, you can schedule tasks flexibly, monitor them with the built-in dashboard, and ensure reliable execution.

Key takeaways:

  • Recurring jobs use cron expressions to schedule tasks.

  • AddOrUpdate prevents duplicate recurring jobs.

  • Jobs should be idempotent, monitored, and logged.

  • Hangfire scales to multiple servers and works well in distributed systems.

By following best practices, Hangfire can handle almost any enterprise background job scenario efficiently.