jaytwo.BackgroundCron 0.1.0-beta-20251115221000

This is a prerelease version of jaytwo.BackgroundCron.
dotnet add package jaytwo.BackgroundCron --version 0.1.0-beta-20251115221000
                    
NuGet\Install-Package jaytwo.BackgroundCron -Version 0.1.0-beta-20251115221000
                    
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="jaytwo.BackgroundCron" Version="0.1.0-beta-20251115221000" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="jaytwo.BackgroundCron" Version="0.1.0-beta-20251115221000" />
                    
Directory.Packages.props
<PackageReference Include="jaytwo.BackgroundCron" />
                    
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 jaytwo.BackgroundCron --version 0.1.0-beta-20251115221000
                    
#r "nuget: jaytwo.BackgroundCron, 0.1.0-beta-20251115221000"
                    
#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 jaytwo.BackgroundCron@0.1.0-beta-20251115221000
                    
#: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=jaytwo.BackgroundCron&version=0.1.0-beta-20251115221000&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=jaytwo.BackgroundCron&version=0.1.0-beta-20251115221000&prerelease
                    
Install as a Cake Tool

jaytwo.BackgroundCron

Lightweight, resilient, and distributed-friendly cron job scheduling for .NET.

NuGet Version NuGet Downloads License: MIT

jaytwo.BackgroundCron provides a framework for scheduling recurring tasks in ASP.NET Core applications using cron expressions and time zones. It integrates with IHostedService, supports distributed locking, and allows flexible configuration.

This library is designed to simplify reliable scheduling of background jobs in .NET Core apps without needing external schedulers. It emphasizes idempotent, resilient execution across potentially distributed environments.

View source on GitHub

Features

  • Uses standard Unix cron expressions (e.g. "0 6 * * *" = daily at 6am)
  • Time zone–aware scheduling using TZDB time zones, including daylight saving time support (e.g. America/Los_Angeles, Europe/London)
  • Distributed locking support for multi-instance deployments
  • Runtime task toggling via ProcessorEnabledCallback (e.g. for feature flags)
  • Supports using IDistributedCache as a timestamp store
  • Configurable behavior for missed executions (e.g. skip or catch up)
  • Health check interface for schedule metadata and task-specific status

Background

Occasionally, I need to schedule a recurring task in my application—something simple, like running a job every day at 6am.

I don’t want to maintain a separate scheduler app (e.g. Hangfire). I need it to work across multiple instances. I don’t want to worry about the details of scheduling, locking, or time zones. The goal is to make it work simply and reliably.

Cron is the de facto standard for scheduling, and the Time Zone Database is the de facto standard for time zone handling—so I built this library on top of both.

Rolling your own BackgroundService starts out easy enough—that’s exactly how this project began! But soon, you’re dealing with things like:

  • Time zones and daylight saving time transitions
  • Ensuring the task only runs once, even when deployed across multiple instances

After copying and pasting the same boilerplate into a few different projects, I decided to extract the logic into a reusable library.

Don’t get me wrong—Hangfire is great, but it’s a bit heavy-handed for a simple recurring task. Quartz.NET is also a powerful scheduler, but getting it to work reliably across multiple instances takes a bit more effort. Both are far more mature and widely adopted than this little tool will ever be—but for my use case, I just wanted something lightweight that worked.

Installation

Add the NuGet package:

PM> Install-Package jaytwo.BackgroundCron

Usage

1. Create your task processor

Implement the ICronTaskProcessor interface:

public class SampleCronTaskProcessor : ICronTaskProcessor
{
    private readonly ILogger _logger;

    public SampleCronTaskProcessor(ILogger<SampleCronTaskProcessor> logger)
    {
        _logger = logger;
    }

    public Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation($"{nameof(SampleCronTaskProcessor)} Executed.");
        return Task.CompletedTask;
    }

    public Task<object> HealthCheckAsync(CancellationToken stoppingToken = default)
    {
        object result = new { hello = "world" };
        return Task.FromResult(result);
    }
}

2. Configure in appsettings.json

To run at 6am daily in the America/Denver time zone:

// in appsettings.json:

{
  "SampleCronTask": {
    "TaskName": "MyTask",
    "CronExpression": "0 6 * * *",
    "TimeZoneId": "America/Denver"
  }
}

3. Register services in Program.cs or Startup.cs

Be sure to register the required dependencies, including:

  • An IDistributedLockProvider
  • Either an ITimestampStore or IDistributedCache
builder.Services.AddSingleton<IDistributedCache, MemoryDistributedCache>(); // For demo/single-instance deployments only
builder.Services.AddSingleton<IDistributedLockProvider, InProcessLockProvider>(); // For demo/single-instance deployments only
builder.Services.AddBackgroundCronService<SampleCronTaskProcessor>("SampleCronTask"); // Matches the config section name in appsettings.json

For production use, implement IDistributedLockProvider and IDistributedCache (or a custom ITimestampStore) with persistent, shared infrastructure like Redis or a distributed database.

Health Check Example

This is useful for exposing runtime status to observability tools or dashboards.

Optionally expose task status through an API:

public class HomeController : Controller
{
    private readonly SampleCronTaskProcessor _processor;
    private readonly BackgroundCronService<SampleCronTaskProcessor> _cronService;

    public HomeController(SampleCronTaskProcessor processor, BackgroundCronService<SampleCronTaskProcessor> cronService)
    {
        _processor = processor;
        _cronService = cronService;
    }

    [Route("/health")]
    public async Task<object> Index(CancellationToken token) => new
    {
        SampleCronTaskProcessor = await _processor.HealthCheckAsync(token),
        BackgroundCronService = await _cronService.HealthCheckAsync(token),
    };
}

Notes

  • Tasks are run in-process. For long-running or unreliable operations, ensure idempotency and use retry logic as needed.
  • Multiple tasks can be registered with separate configuration sections.
  • RunOnMissedExecution allows running the most recent missed execution if the app was offline (earlier missed runs are skipped).
  • Task execution is automatically skipped if already executed by another instance.
  • Timestamp and lock keys are uniquely scoped per task name and processor type.

Made with ♥ by Jake — Licensed under the MIT License

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  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. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0-beta-20251115221000 211 11/16/2025
0.1.0-beta-20251102144556 177 11/2/2025
0.1.0-beta-20251017085447 108 10/17/2025
0.1.0-beta-20251017002331 128 10/17/2025
0.1.0-beta-20250526214340 289 5/27/2025