I have a Hangfire application written on .NET 8 and hosted on IIS. It's used to run a daily scheduled job. Problem is that after some time the application shuts down and does not start again, so job does not run.
I did everything in the Hangfire documentation section "If nothing works for you…, but it still does not work. I recycle the app pool / restart, but application does not start again.
IIS:
installed Application Initialization module
application pool:
set .NET CLR version to 4.0
set managed pipeline mode to 4.0
set start mode to AlwaysRunning
set Idle Time-Out (minutes) to 0
site:
set Preload Enabled to true
Configuration Manager → system.webServer/applicationInitialization
From: ApplicationHost.config
set doAppInitAfterRestart to True
added hostName and initializationPage to Collection Editor
What am I missing?
Windows Server 2019, IIS version 10.
You should host your hangfire server in different application - windows service for example. And use Web API just to host hangfire dashboard.
Setup would look something like this:
For Windows service:
var hostbuilder = Host.CreateDefaultBuilder(args);
var host = hostbuilder.UseWindowsService().
ConfigureServices((hostContext, services) =>
{
var storageOptions = new SqlServerStorageOptions
{
CommandBatchMaxTimeout = TimeSpan.FromMinutes(30),
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
QueuePollInterval = TimeSpan.Zero,
UseRecommendedIsolationLevel = true,
DisableGlobalLocks = true,
EnableHeavyMigrations = true,
};
services.AddHangfire(c => c
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseSqlServerStorage(() => new SqlConnection(connectionString), storageOptions));
services.AddHangfireServer(a =>
{
a.WorkerCount = Math.Min(Environment.ProcessorCount * hangfireConfiguration.WorkerCount, 10);
a.Queues = new[] { "default", "lowprio" };
});
}).Build();
await host.RunAsync();
And for the Web API you just configure the Hangfire dashboard , use the same code from above just without adding the hangfire servers.
Something like this:
var builder = WebApplication.CreateBuilder(args);
var storageOptions = new SqlServerStorageOptions
{
CommandBatchMaxTimeout = TimeSpan.FromMinutes(30),
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
QueuePollInterval = TimeSpan.Zero,
UseRecommendedIsolationLevel = true,
PrepareSchemaIfNecessary = false
};
builder.Services..AddHangfire(c => c
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseSqlServerStorage(() => new SqlConnection(connectionString), storageOptions));
var app = builder.Build();
var hangfireStorage = new SqlServerStorage(() => new SqlConnection(connectionString), storageOptions);
var dashboardOptions = new DashboardOptions { Authorization = new[] { new AllowAllAuthorizationFilter() } };
app.UseHangfireDashboard("/dashboard", dashboardOptions, hangfireStorage);
await app.RunAsync();
}
public class AllowAllAuthorizationFilter : IDashboardAuthorizationFilter
{
public bool Authorize(DashboardContext context)
{
return true;
}
}
PS Don't forget to use same connection string. Your server and your dashboard will look into the same database.
Achieving always on on IIS is really challenging and depends on a lot of things - this way is much easier - widows service is always running.