I created the Blazor WASM (Abp Commercial v.8.3.2) solution with Abp Studio, I use Hangfire to manage my ETL workflow.
The workflows must run on a single server (ETL Server - Abp with Hangfire) and must have the dashboard available.
The API endpoints, however, will be distributed across two servers (Application Servers - Only API), without Redis, which will run behind a load balancer. The Application Servers are intended for use with the Blazor WASM frontend.
I tried creating a configuration that enables and disables Hangfire via application settings, but I can't disable Hangfire on the application servers. Furthermore, I can't force the ABP background workers/jobs to run only on the ETL Server (Abp with Hangifire).
I need help and explanations on how to implement this configuration.
I'm attaching an example image of the final infrastructure.
Thanks
22 Answer(s)
-
0
To achieve your desired infrastructure—where only the ETL server runs Hangfire and background jobs, and the API servers do not—you need to:
- Disable Hangfire and Background Job Execution on API Servers
- Enable Hangfire and Background Job Execution only on the ETL Server
- Ensure all jobs are queued in a shared database, so only the ETL server processes them
- Expose the Hangfire Dashboard only on the ETL server
Step-by-Step Solution
1. Shared Database for Background Jobs
- Configure all your servers (API and ETL) to use the same database for background jobs (Hangfire/ABP).
- This ensures jobs enqueued from any API server are visible to the ETL server.
2. Disable Background Job Execution on API Servers
In your API servers'
appsettings.json
or environment-specific settings, add:"AbpBackgroundJobs": { "IsJobExecutionEnabled": false }
Or, in your module configuration:
Configure<AbpBackgroundJobOptions>(options => { options.IsJobExecutionEnabled = false; });
Do NOT add or configure Hangfire server on the API servers:
- Do not call
context.Services.AddHangfireServer()
on API servers. - Do not call
app.UseAbpHangfireDashboard()
on API servers.
3. Enable Hangfire and Background Job Execution on ETL Server
On the ETL server, enable Hangfire and background job execution:
Configure<AbpBackgroundJobOptions>(options => { options.IsJobExecutionEnabled = true; }); context.Services.AddHangfire(config => { config.UseSqlServerStorage(Configuration.GetConnectionString("Default")); }); context.Services.AddHangfireServer();
And in your
OnApplicationInitialization
:app.UseAbpHangfireDashboard("/hangfire");
4. Environment-based Configuration
Use environment variables or separate
appsettings.{Environment}.json
files to control which servers enable Hangfire/background jobs.Example:
- On API servers:
IsJobExecutionEnabled = false
, no Hangfire server. - On ETL server:
IsJobExecutionEnabled = true
, Hangfire server enabled.
5. Hangfire Dashboard
- Only expose the Hangfire dashboard on the ETL server.
- Secure it as needed (e.g., with authentication).
Summary
- API servers: Only enqueue jobs, do not process them.
- ETL server: Processes jobs and hosts the Hangfire dashboard.
- All use the same database for job storage.
Sources
- Moving Background Job Execution To A Separate Application (ABP Community)
- Optimizing for Production: Background Jobs
- ABP Hangfire Integration
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
-
0
These examples replicate the ABP solution, I need to be able to use a single ABP solution and control the features based on the deployment (application settings) or publishing profile.
-
0
hi
Can you try setting
IsJobExecutionEnabled
totrue
in one app and disabling it in other apps?Thanks.
https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobOptions.cs#L15
-
0
But how do I register Hangifire only in the version to be released on ETL Server?
I tried creating a JobManagerModule that derives from AbpModule in which I try to register Hangfire based on the app settings, but it seems like it always needs to be registered in the application servers as well.
Example of my code:
Thanks
P.S. I sent you the solution.
-
0
hi
I will check your project.
Thanks.
-
0
-
0
I will try, however my first problem is that I can't use the application settings to register Hangfire or not.
Thanks
-
0
-
0
Sorry, I don't have time to test the proposed solution yet. Could you check if it works in the project already shared? If not, I'll do it tomorrow. In the meantime, I'll keep the ticket open.
Thanks
-
0
Ok, I will let the question remain open.
Thanks.
-
0
I tried, but when I disable it from appsettings I get this error:
Volo.Abp.AbpInitializationException: An error occurred during the initialize Volo.Abp.Modularity.OnPreApplicationInitializationModuleLifecycleContributor phase of the module Volo.Abp.BackgroundWorkers.Hangfire.AbpBackgroundWorkersHangfireModule, Volo.Abp.BackgroundWorkers.Hangfire, Version=8.3.2.0, Culture=neutral, PublicKeyToken=null: An exception was thrown while activating ?:Volo.Abp.Hangfire.AbpHangfireBackgroundJobServer -> ?:Hangfire.JobStorage.. See the inner exception for details. ---> Autofac.Core.DependencyResolutionException: An exception was thrown while activating ?:Volo.Abp.Hangfire.AbpHangfireBackgroundJobServer -> ?:Hangfire.JobStorage. ---> System.InvalidOperationException: Current JobStorage instance has not been initialized yet. You must set it before using Hangfire Client or Server API. For .NET Core applications please call the
IServiceCollection.AddHangfire
extension method from Hangfire.NetCore or Hangfire.AspNetCore package depending on your application type when configuring the services and ensure service-based APIs are used instead of static ones, likeIBackgroundJobClient
instead ofBackgroundJob
andIRecurringJobManager
instead ofRecurringJob
. at Hangfire.JobStorage.get_Current() in C:\projects\hangfire-525\src\Hangfire.Core\JobStorage.cs:line 42 -
0
-
0
I did it, but it's not clear to me why it's needed since the jobs are not initialized and the worker I made to delete the logs is similar to ExpiredAuditLogDeleterWorker so it shouldn't depend on the presence or absence of HangFire. Can you explain it to me?
Finally, I always see this call in the logs. I thought it was related to the ABP workers, but it doesn't seem to be the case since they should all be down now. Can you tell me what's causing it?
2025-08-12T10:22:32.9921082+02:00 [INF] - (Microsoft.EntityFrameworkCore.Database.Command) Executed DbCommand ("4"ms) [Parameters=["@__p_0='0', @__p_1='1'"], CommandType='Text', CommandTimeout='30']"\r\n""SELECT [a].[Id], [a].[ConcurrencyStamp], [a].[EntityVersion], [a].[ExtraProperties], [a].[IsDefault], [a].[IsPublic], [a].[IsStatic], [a].[Name], [a].[NormalizedName], [a].[TenantId]\r\nFROM [AbpRoles] AS [a]\r\nWHERE [a].[TenantId] IS NULL\r\nORDER BY [a].[Id]\r\nOFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY" 2025-08-12T10:22:32.9936939+02:00 [INF] - (Microsoft.EntityFrameworkCore.Update) Saved 2 entities to in-memory store. 2025-08-12T10:22:42.9977294+02:00 [INF] - (Microsoft.EntityFrameworkCore.Database.Command) Executed DbCommand ("4"ms) [Parameters=["@__p_0='0', @__p_1='1'"], CommandType='Text', CommandTimeout='30']"\r\n""SELECT [a].[Id], [a].[ConcurrencyStamp], [a].[EntityVersion], [a].[ExtraProperties], [a].[IsDefault], [a].[IsPublic], [a].[IsStatic], [a].[Name], [a].[NormalizedName], [a].[TenantId]\r\nFROM [AbpRoles] AS [a]\r\nWHERE [a].[TenantId] IS NULL\r\nORDER BY [a].[Id]\r\nOFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY" 2025-08-12T10:22:42.9995277+02:00 [INF] - (Microsoft.EntityFrameworkCore.Update) Saved 2 entities to in-memory store.
Thanks
-
0
hi
Hangfire requires you to set up a valid storage so that you can add new jobs to it, even if they aren't executed in the current app.
-
0
Ok, and for my second question?
-
0
Saved 2 entities to in-memory store.
This log only outputs if you are using
Microsoft.EntityFrameworkCore.InMemory
-
0
[maliming] said:
Saved 2 entities to in-memory store.
This log only outputs if you are using
Microsoft.EntityFrameworkCore.InMemory
But who generates them since I have deactivated all workers and jobs?
-
0
Hi
Maybe the DB call was not invoked by the background job/worker.
-
0
I don't use InMemory storage in my code, and the query looks like it's an ABP (Roles) module. It runs every 10 seconds.
I've been asked to not have Workers running in the application servers and only have them in the ETL Server. Can you help me figure out which one it is so I can block it if possible or open an issue about it?
Thanks
P.s. You can use the repo shared with you for debug and test.
-
0
hi
Try to disable/remove all
HealthCheck
from your project.Keyword:
HealthCheck
private void ConfigureHealthChecks(ServiceConfigurationContext context) { context.Services.AddMyProjectNameHealthChecks(); }
-
0
Yes it was HealthCheck calling IdentityRoleRepository.GetListAsync
I'm reporting a small problem with IdentityRole Repository (and maybe others too) The interface contains includeDetails = false
But in the implementation, it's True.
If the repository is injected with the interface, False wins. If it's injected with the class, True wins.
I recommend that you align your implementation code with the defaults in the interface.
Thanks!
-
0
Thanks. I will fix it,