Skip to content

Hangfire Configuration

alt MassTransit on NuGet

MassTransit supports Hangfire as a message scheduler, providing reliable scheduled message delivery with a persistent storage backend. Hangfire is particularly useful when you need:

  • Reliable scheduled message delivery that survives application restarts
  • A built-in dashboard to monitor scheduled jobs
  • Integration with an existing Hangfire installation in your application
  • Support for recurring jobs using cron expressions

Install the required packages:

Terminal window
dotnet add package MassTransit.Hangfire

Automatically adds Hangfire.Core.

You’ll also need a Hangfire storage package based on your chosen backend:

Terminal window
# Choose one of the following:
dotnet add package Hangfire.SqlServer # SQL Server
dotnet add package Hangfire.Redis.StackExchange # Redis
dotnet add package Hangfire.PostgreSQL # PostgreSQL

Hangfire requires persistent storage to survive application restarts. Choose your storage backend:

services.AddHangfire(h =>
{
h.UseRecommendedSerializerSettings();
h.UseMemoryStorage();
});
services.AddHangfire(h =>
{
h.UseRecommendedSerializerSettings();
h.UseSqlServerStorage("Server=.;Database=Hangfire;");
});
services.AddHangfire(h =>
{
h.UseRecommendedSerializerSettings();
h.UseRedisStorage("localhost:6379");
});
services.AddHangfire(h =>
{
h.UseRecommendedSerializerSettings();
h.UsePostgreSqlStorage(options =>
{
options.UseNpgsqlConnection("Host=localhost;Database=hangfire;Username=postgres;Password=password");
});
});

Configure MassTransit to use Hangfire as the message scheduler:

services.AddMassTransit(x =>
{
x.AddPublishMessageScheduler();
x.AddHangfireConsumers();
x.UsingRabbitMq((context, cfg) =>
{
cfg.UsePublishMessageScheduler();
cfg.ConfigureEndpoints(context);
});
});

The key components are:

  1. AddPublishMessageScheduler() - Registers the message scheduler in the container
  2. AddHangfireConsumers() - Adds the Hangfire consumers that handle scheduling operations
  3. UsePublishMessageScheduler() - Configures the transport to use the scheduler

Inject ConsumeContext and use the scheduling methods:

public class OrderProcessingConsumer :
IConsumer<ProcessOrder>
{
public async Task Consume(ConsumeContext<ProcessOrder> context)
{
// Schedule a message to be sent after a delay
await context.ScheduleSend<SendNotification>(
DateTime.UtcNow + TimeSpan.FromHours(1),
new NotificationMessage
{
OrderId = context.Message.OrderId,
Message = "Your order has been processed"
});
}
}

Inject IMessageScheduler from the container:

public class NotificationService
{
private readonly IMessageScheduler _scheduler;
public NotificationService(IMessageScheduler scheduler)
{
_scheduler = scheduler;
}
public async Task SendDelayedNotification(string email, string message)
{
await _scheduler.SchedulePublish(
DateTime.UtcNow.AddDays(1),
new NotificationMessage { Email = email, Message = message });
}
}
  • ScheduleSend - Sends directly to a specific endpoint (useful for targeting a specific queue)
  • SchedulePublish - Publishes to the message bus (useful for pub/sub scenarios)
// Send to a specific endpoint
await context.ScheduleSend(
new Uri("queue:notification-service"),
delay,
message);
// Publish for subscribers
await context.SchedulePublish(delay, message);

Hangfire supports recurring jobs using cron expressions:

public class DailyReportSchedule :
DefaultRecurringSchedule
{
public DailyReportSchedule()
{
ScheduleId = "daily-report";
CronExpression = "0 0 9 * * ?"; // Every day at 9 AM
}
}
public record GenerateDailyReport;

To schedule a recurring message:

public class ReportSchedulerService
{
private readonly IRecurringMessageScheduler _scheduler;
public ReportSchedulerService(IRecurringMessageScheduler scheduler)
{
_scheduler = scheduler;
}
public async Task ScheduleDailyReports()
{
var inputAddress = new Uri("queue:report-generator");
await _scheduler.ScheduleRecurringSend(
inputAddress,
new DailyReportSchedule(),
new GenerateDailyReport());
}
}

To cancel a recurring schedule:

await _scheduler.CancelScheduledRecurringMessage("daily-report", null);

To expose the Hangfire dashboard in your ASP.NET Core application:

// In Program.cs
var app = builder.Build();
app.UseHangfireDashboard();
app.MapHealthChecks("/health");

In production, you should secure the dashboard:

app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[] { new HangfireAuthFilter() }
});
public class HangfireAuthFilter : IDashboardAuthorizationFilter
{
public bool Authorize(DashboardContext context)
{
// Implement your authorization logic
return context.GetHttpContext().User.Identity?.IsAuthenticated == true;
}
}

If your app is hosted at a base path:

app.UseHangfireDashboard("/myapp/hangfire");

Hangfire can be used for scheduled message redelivery (second-level retries):

services.AddMassTransit(x =>
{
x.AddConsumer<OrderProcessingConsumer>();
x.AddConfigureEndpointsCallback((context, name, cfg) =>
{
cfg.UseScheduledRedelivery(r => r
.Intervals(
TimeSpan.FromMinutes(5),
TimeSpan.FromMinutes(15),
TimeSpan.FromMinutes(30)));
cfg.UseMessageRetry(r => r.Immediate(3));
});
x.UsingRabbitMq((context, cfg) =>
{
cfg.UsePublishMessageScheduler();
cfg.ConfigureEndpoints(context);
});
});
Program.cs
var builder = WebApplication.CreateBuilder(args);
// Configure Hangfire with SQL Server storage
builder.Services.AddHangfire(h => h
.UseRecommendedSerializerSettings()
.UseSqlServerStorage(builder.Configuration.GetConnectionString("Hangfire")));
builder.Services.AddHangfireServer();
// Add MassTransit with Hangfire
builder.Services.AddMassTransit(x =>
{
x.AddPublishMessageScheduler();
x.AddConsumers(typeof(Program).Assembly);
x.AddHangfireConsumers();
x.UsingRabbitMq((context, cfg) =>
{
cfg.Host(builder.Configuration.GetConnectionString("RabbitMq"));
cfg.UsePublishMessageScheduler();
cfg.ConfigureEndpoints(context);
});
});
var app = builder.Build();
// Enable Hangfire Dashboard
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[] { new HangfireAuthFilter() }
});
app.UseHealthChecks("/health");
app.Run();