jaytwo.BackgroundCron
0.1.0-beta-20251115221000
dotnet add package jaytwo.BackgroundCron --version 0.1.0-beta-20251115221000
NuGet\Install-Package jaytwo.BackgroundCron -Version 0.1.0-beta-20251115221000
<PackageReference Include="jaytwo.BackgroundCron" Version="0.1.0-beta-20251115221000" />
<PackageVersion Include="jaytwo.BackgroundCron" Version="0.1.0-beta-20251115221000" />
<PackageReference Include="jaytwo.BackgroundCron" />
paket add jaytwo.BackgroundCron --version 0.1.0-beta-20251115221000
#r "nuget: jaytwo.BackgroundCron, 0.1.0-beta-20251115221000"
#:package jaytwo.BackgroundCron@0.1.0-beta-20251115221000
#addin nuget:?package=jaytwo.BackgroundCron&version=0.1.0-beta-20251115221000&prerelease
#tool nuget:?package=jaytwo.BackgroundCron&version=0.1.0-beta-20251115221000&prerelease
jaytwo.BackgroundCron
Lightweight, resilient, and distributed-friendly cron job scheduling for .NET.
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.
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
IDistributedCacheas 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
ITimestampStoreorIDistributedCache
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
IDistributedLockProviderandIDistributedCache(or a customITimestampStore) 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.
RunOnMissedExecutionallows 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 | Versions 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. |
-
.NETStandard 2.1
- jaytwo.DistributedLocks (>= 0.1.0-beta-20251113173008)
- jaytwo.LocalTime (>= 0.1.0-beta-20251008230428)
- Microsoft.Extensions.Caching.Abstractions (>= 3.1.32)
- Microsoft.Extensions.Hosting.Abstractions (>= 3.1.32)
- Microsoft.Extensions.Logging.Abstractions (>= 3.1.32)
-
net6.0
- jaytwo.DistributedLocks (>= 0.1.0-beta-20251113173008)
- jaytwo.LocalTime (>= 0.1.0-beta-20251008230428)
- Microsoft.Extensions.Caching.Abstractions (>= 6.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 6.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 6.0.0)
-
net8.0
- jaytwo.DistributedLocks (>= 0.1.0-beta-20251113173008)
- jaytwo.LocalTime (>= 0.1.0-beta-20251008230428)
- Microsoft.Extensions.Caching.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
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 |