Robust. Adaptive. Precise.
TickerQ is a fast, reflection-free background task scheduler for .NET — built with source generators, EF Core integration, cron + time-based execution, and a real-time dashboard.
📚 Full Docs: https://tickerq.arcenox.com
Note: As of v2.2.0, all TickerQ packages are versioned together — even if a package has no changes — to keep the ecosystem in sync. Always update all packages to the same version.
- Time and Cron Scheduling
- Stateless Core with source generator
- EF Core Persistence
- Live Dashboard UI
- Retry Policies & Throttling
- Dependency Injection support
- Multi-node distributed coordination
dotnet add package TickerQ
dotnet add package TickerQ.EntityFrameworkCore
dotnet add package TickerQ.Dashboard
builder.Services.AddTickerQ(options =>
{
options.SetMaxConcurrency(4); // Optional
options.SetExceptionHandler<MyExceptionHandler>(); // Optional
options.AddOperationalStore<MyDbContext>(efOpt =>
{
efOpt.UseModelCustomizerForMigrations(); // Applies custom model customization only during EF Core migrations
efOpt.CancelMissedTickersOnApplicationRestart(); // Useful in distributed mode
}); // Enables EF-backed storage
options.AddDashboard(basePath: "/tickerq-dashboard"); // Dashboard path
options.AddDashboardBasicAuth(); // Enables simple auth
});
app.UseTickerQ(); // Activates job processor
To control when the job processor starts use the TickerQStartMode overload
app.UseTickerQ(TickerQStartMode.Manual);
ITickerHost tickerHost = app.Services.GetRequiredService<ITickerHost>(); // Resolve the Singleton service ITickerHost from the IServiceProvider.
tickerHost.Start(); // Invoke the start method to manually start TickerQ
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options) { }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Apply TickerQ entity configurations explicitly
builder.ApplyConfiguration(new TimeTickerConfigurations());
builder.ApplyConfiguration(new CronTickerConfigurations());
builder.ApplyConfiguration(new CronTickerOccurrenceConfigurations());
// Alternatively, apply all configurations from assembly:
// builder.ApplyConfigurationsFromAssembly(typeof(TimeTickerConfigurations).Assembly);
}
}
💡 Recommendation:
UseUseModelCustomizerForMigrations()
to cleanly separate infrastructure concerns from your core domain model, especially during design-time operations like migrations.
Note: If you're using third-party libraries (e.g., OpenIddict) that also overrideIModelCustomizer
, you must either merge customizations or fall back to manual configuration insideOnModelCreating()
to avoid conflicts.
public class CleanupJobs
{
[TickerFunction(functionName: "CleanupLogs", cronExpression: "0 0 * * *" )]
public void CleanupLogs()
{
// Runs every midnight
}
}
This uses a cron expression to run daily at midnight.
public class NotificationJobs
{
[TickerFunction(functionName: "SendWelcome")]
public Task SendWelcome(TickerFunctionContext<string> tickerContext ,CancellationToken ct)
{
Console.WriteLine(tickerContext.Request); // Output: User123
return Task.CompletedTask;
}
}
Then schedule it:
await _timeTickerManager.AddAsync(new TimeTicker
{
Function = "SendWelcome",
ExecutionTime = DateTime.UtcNow.AddMinutes(1),
Request = TickerHelper.CreateTickerRequest<string>("User123"),
Retries = 3,
RetryIntervals = new[] { 30, 60, 120 }, // Retry after 30s, 60s, then 2min
// Optional batching
BatchParent = Guid.Parse("...."),
BatchRunCondition = BatchRunCondition.OnSuccess
});
public class ReportJobs
{
private readonly IReportService _reportService;
public ReportJobs(IReportService reportService)
{
_reportService = reportService;
}
[TickerFunction(functionName: "GenerateDailyReport", cronExpression: "0 6 * * *")]
public async Task GenerateDailyReport()
{
await _reportService.GenerateAsync();
}
}
Check out Dashboard Overview: TickerQ-Dashboard-Examples
Enabled by adding:
options.AddDashboard(basePath: "/tickerq-dashboard");
options.AddDashboardBasicAuth(); // Optional
Accessible at /tickerq-dashboard
, it shows:
- System status
- Active tickers
- Job queue state
- Cron ticker stats
- Execution history
- Trigger/cancel/edit jobs live
Auth config (optional):
"TickerQBasicAuth": {
"Username": "admin",
"Password": "admin"
}
TickerQ supports:
- Retries per job
- Retry intervals (
RetryIntervals
) - Distributed locking (EF mode only)
- Job ownership tracking across instances
- Cooldown on job failure
await _cronTickerManager.AddAsync(new CronTicker
{
Function = "CleanupLogs",
Expression = "0 */6 * * *", // Every 6 hours
Retries = 2,
RetryIntervals = new[] { 60, 300 }
});
- Use
[TickerFunction]
to register jobs - Use
FunctionName
consistently across schedule and handler - Use
CancellationToken
for graceful cancellation - Use
Request
to pass dynamic data to jobs - If you are building this project locally, you must replace the
$(PackageVersion)
with any version NuGet package version (ideally the latest). - If you are getting random 403 responses, make sure that you don't have any filter in some endpoint that might be triggering it, thus causing issues with TickerQ's dashboard. Check this issue for more details.
We want to acknowledge the individuals and organizations who support the development of TickerQ through OpenCollective. Your contributions help us maintain and grow this project. If you'd like to support, check out the tiers below and join the community!
Become a Sponsor or Backer on OpenCollective
Become a gold sponsor and get your logo here with a link to your site.
Become a silver sponsor and get your logo here with a link to your site.
Become a bronze sponsor and get your logo here with a link to your site.
Become a backer and get your image on our README on GitHub with a link to your site.
PRs, ideas, and issues are welcome!
- Fork & branch
- Code your change
- Submit a Pull Request
MIT OR Apache 2.0 © Arcenox
You may choose either license to use this software.