NexJob 0.4.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package NexJob --version 0.4.0
                    
NuGet\Install-Package NexJob -Version 0.4.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NexJob" Version="0.4.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NexJob" Version="0.4.0" />
                    
Directory.Packages.props
<PackageReference Include="NexJob" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add NexJob --version 0.4.0
                    
#r "nuget: NexJob, 0.4.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package NexJob@0.4.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=NexJob&version=0.4.0
                    
Install as a Cake Addin
#tool nuget:?package=NexJob&version=0.4.0
                    
Install as a Cake Tool

<div align="center">

<br/>

███╗   ██╗███████╗██╗  ██╗     ██╗ ██████╗ ██████╗
████╗  ██║██╔════╝╚██╗██╔╝     ██║██╔═══██╗██╔══██╗
██╔██╗ ██║█████╗   ╚███╔╝      ██║██║   ██║██████╔╝
██║╚██╗██║██╔══╝   ██╔██╗ ██   ██║██║   ██║██╔══██╗
██║ ╚████║███████╗██╔╝ ██╗╚█████╔╝╚██████╔╝██████╔╝
╚═╝  ╚═══╝╚══════╝╚═╝  ╚═╝ ╚════╝  ╚═════╝ ╚═════╝

Background jobs for .NET that stay out of your way.

NuGet NuGet Downloads Build License: MIT .NET

<br/>

</div>


What is NexJob?

NexJob is a modern, open-source background job framework for .NET built for predictable execution, time-sensitive workflows, and explicit failure handling.

Unlike Hangfire — which relies on serialization magic and delayed async — NexJob is async/await native, fully type-safe, and stateless. Jobs run in isolated DI scopes with zero hidden state. Storage is the source of truth. Failures are explicit.


⚡ 30-Second Quick Start

1. Define a job

public class SendInvoiceJob : IJob<SendInvoiceInput>
{
    private readonly IEmailService _email;
    public SendInvoiceJob(IEmailService email) => _email = email;

    public async Task ExecuteAsync(SendInvoiceInput input, CancellationToken ct)
        => await _email.SendAsync(input.Email, "Invoice attached", ct);
}

2. Register

builder.Services.AddNexJob(builder.Configuration)
                .AddNexJobJobs(typeof(Program).Assembly);

3. Enqueue

await scheduler.EnqueueAsync<SendInvoiceJob, SendInvoiceInput>(
    new(orderId, email));

Done. The job executes immediately on an available worker.


🔥 Why NexJob?

Feature Benefit
Predictable execution No serialization magic, no hidden state. Jobs run in isolated DI scopes.
Deadline support (deadlineAfter) Mark jobs expired if not started in time — essential for time-sensitive operations.
Automatic dead-letter handling Failed jobs trigger explicit handlers for alerts, compensation, or cleanup.
Low-latency dispatch Wake-up signaling eliminates polling delay on local enqueue. 2.87× faster than Hangfire.
All storage free PostgreSQL, SQL Server, Redis, MongoDB — no paid adapters or license walls.
Async/await native True async from the ground up. No serialization hacks.
Live config Pause queues, adjust workers, change polling — all at runtime without restart.
Schema migrations Auto-migrations on startup. No manual SQL. No deployment breakage.

⚔️ NexJob vs Hangfire

NexJob Hangfire
License MIT LGPL / paid Pro
Async/await native ❌ serialization-based
Deadline support deadlineAfter
Dead-letter handlers ✅ DI-based
Priority queues
Resource throttling [Throttle]
Per-job retry config [Retry]
Execution windows
Live config
Schema migrations ✅ auto
Graceful shutdown
OpenTelemetry ✅ built-in
Payload versioning IJobMigration
All adapters free ❌ Redis/MongoDB paid
Storage providers 5 (all open-source) 3
Enqueue latency 9.28 μs 26.63 μs

📦 Installation

# Core
dotnet add package NexJob

# Storage (pick one — all free)
dotnet add package NexJob.Postgres
dotnet add package NexJob.SqlServer
dotnet add package NexJob.Redis
dotnet add package NexJob.MongoDB

# Dashboard
dotnet add package NexJob.Dashboard

Features in Depth

Scheduling

Fire and forget

await scheduler.EnqueueAsync<SendInvoiceJob, SendInvoiceInput>(new(orderId, email));

Delayed

await scheduler.ScheduleAsync<SendInvoiceJob, SendInvoiceInput>(
    new(orderId, email), 
    delay: TimeSpan.FromMinutes(5));

Recurring (cron)

await scheduler.RecurringAsync<ReportJob, ReportInput>(
    id: "monthly",
    input: new(DateTime.UtcNow.Month),
    cron: "0 9 1 * *");

Continuations (chaining)

var paymentId = await scheduler.EnqueueAsync<ProcessPaymentJob, PaymentInput>(input);
await scheduler.ContinueWithAsync<SendReceiptJob, ReceiptInput>(paymentId, receiptInput);

Reliability

Global retry policy

By default, jobs retry with exponential backoff. Configure via appsettings.json:

{ "NexJob": { "MaxAttempts": 5 } }

Per-job retry

[Retry(5, InitialDelay = "00:00:30", Multiplier = 2.0, MaxDelay = "01:00:00")]
public class PaymentJob : IJob<PaymentInput> { ... }

Dead-letter handler

Automatically handle jobs that exhaust retries:

public class PaymentDeadLetterHandler : IDeadLetterHandler<PaymentJob>
{
    public async Task HandleAsync(JobRecord failedJob, Exception lastException, CancellationToken ct)
        => await _alerts.SendAsync($"Payment failed", ct);
}

builder.Services.AddTransient<IDeadLetterHandler<PaymentJob>, PaymentDeadLetterHandler>();

Graceful shutdown

Jobs complete naturally on SIGTERM. Strays are requeued by orphan watcher.


Deadlines

Jobs not executed within the deadline are marked Expired and skipped:

await scheduler.EnqueueAsync<PaymentJob, PaymentInput>(
    input,
    deadlineAfter: TimeSpan.FromMinutes(5));

Observability

OpenTelemetry (zero config)

builder.Services.AddOpenTelemetry()
    .WithTracing(t => t.AddSource("NexJob"))
    .WithMetrics(m => m.AddMeter("NexJob"));

Metrics: nexjob.jobs.enqueued, nexjob.jobs.succeeded, nexjob.jobs.failed, nexjob.jobs.expired, nexjob.job.duration.

Job context

public class ImportJob : IJob<ImportInput>
{
    private readonly IJobContext _ctx;
    public ImportJob(IJobContext ctx) => _ctx = ctx;

    public async Task ExecuteAsync(ImportInput input, CancellationToken ct)
    {
        await _ctx.ReportProgressAsync(0, "Starting...", ct);
        // ... do work ...
        await _ctx.ReportProgressAsync(100, "Done.", ct);
    }
}

Job tags

await scheduler.EnqueueAsync<SendInvoiceJob, SendInvoiceInput>(
    input,
    tags: ["tenant:acme", $"invoice:{invoiceId}"]);

var jobs = await scheduler.GetJobsByTagAsync("tenant:acme");

Concurrency & Throttling

Resource throttling

[Throttle(resource: "stripe", maxConcurrent: 3)]
public class ChargeCardJob : IJob<ChargeInput> { ... }

Execution windows

Restrict queues to specific times:

{
  "NexJob": {
    "Queues": [
      {
        "Name": "reports",
        "Workers": 2,
        "ExecutionWindow": {
          "StartTime": "22:00",
          "EndTime": "06:00",
          "TimeZone": "America/Sao_Paulo"
        }
      }
    ]
  }
}

Payload Versioning

Handle evolving job inputs gracefully:

[SchemaVersion(2)]
public class SendInvoiceJob : IJob<SendInvoiceInputV2> { ... }

public class Migration : IJobMigration<SendInvoiceInputV1, SendInvoiceInputV2>
{
    public SendInvoiceInputV2 Migrate(SendInvoiceInputV1 old)
        => new(old.OrderId, old.Email, Language: "en-US");
}

builder.Services.AddJobMigration<SendInvoiceInputV1, SendInvoiceInputV2, Migration>();

Dashboard

Web API:

app.UseNexJobDashboard("/dashboard");

Worker Service / Console App:

services.AddNexJobStandaloneDashboard(configuration);
// Dashboard at http://localhost:5005/dashboard

Configuration

appsettings.json

{
  "NexJob": {
    "Workers": 10,
    "MaxAttempts": 5,
    "PollingInterval": "00:00:05",
    "ShutdownTimeoutSeconds": 30,
    "DefaultQueue": "default",
    "Queues": [
      { "Name": "critical", "Workers": 3 },
      { "Name": "default",  "Workers": 5 }
    ]
  }
}

Code configuration

builder.Services.AddNexJob(builder.Configuration, opt =>
{
    opt.UsePostgres(connectionString);
    opt.Workers = 10;
    opt.MaxAttempts = 5;
});

Storage Providers

All open-source. No license walls.

Package Storage Status
NexJob In-memory Production ready
NexJob.Postgres PostgreSQL 14+ Production ready
NexJob.SqlServer SQL Server 2019+ Production ready
NexJob.Redis Redis 7+ Production ready
NexJob.MongoDB MongoDB 6+ Production ready
NexJob.Oracle Oracle 19c+ Planned

Testing

The in-memory provider is built-in:

services.AddNexJob();  // InMemory by default

Project

Roadmap

v0.4.0  ✅ Wake-up channel · Deadlines · Dead-letter handlers · README updated
v1.0.0  ○ Stable API · production-ready

Contributing

NexJob is open-source. Issues, PRs, and ideas welcome.

git clone git@github.com:oluciano/NexJob.git
cd NexJob && dotnet test

See CONTRIBUTING.md for details.

License

MIT © 2025 Luciano Azevedo


<div align="center"> <br/>

Built with obsession over developer experience.

If Hangfire is the past, NexJob is what comes next.

<br/> </div>

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (6)

Showing the top 5 NuGet packages that depend on NexJob:

Package Downloads
NexJob.Dashboard

Blazor SSR dashboard middleware for NexJob — real-time monitoring of background jobs.

NexJob.Postgres

PostgreSQL storage provider for NexJob — the open-source background job scheduler for .NET 8+.

NexJob.MongoDB

MongoDB storage provider for NexJob — the open-source background job scheduler for .NET 8+.

NexJob.SqlServer

SQL Server storage provider for NexJob.

NexJob.Redis

Redis storage provider for NexJob.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 83 4/8/2026
0.8.0 78 4/8/2026
0.7.0 123 4/7/2026
0.6.0 162 4/3/2026
0.5.2 168 4/1/2026
0.5.1 158 4/1/2026
0.5.0 168 3/31/2026
0.4.0 163 3/31/2026
0.3.2 206 3/28/2026
0.3.1 219 3/28/2026
0.3.0 181 3/27/2026
0.2.0 178 3/27/2026
0.1.1 131 3/27/2026
0.1.0 122 3/26/2026
0.1.0-alpha 123 3/26/2026