Quartz.AspNetCore 4.0.0-alpha.1


title: ASP.NET Core Integration

Quartz.AspNetCore provides integration with ASP.NET Core hosted services.

If you only need the generic host, generic host integration might suffice.

Installation

You need to add NuGet package reference to your project which uses Quartz.

dotnet add package Quartz.AspNetCore

Using

You can host the scheduler by invoking AddQuartzHostedService on the web application builder. This adds a hosted Quartz server into the ASP.NET Core process that is started and stopped based on the application's lifetime.

AddQuartzHostedService lives in the core Quartz package. Quartz 3's AddQuartzServer, which registered the hosted service and a health check together, is gone — call AddQuartzHealthChecks for the health check.

See Quartz documentation to learn more about configuring Quartz scheduler, jobs and triggers.

Example Program.cs configuration

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

builder.AddQuartz(q =>
{
    // base Quartz scheduler, job and trigger configuration
});

// ASP.NET Core hosting
builder.AddQuartzHostedService(options =>
{
    // when shutting down we want jobs to complete gracefully
    options.WaitForJobsToComplete = true;
});

WebApplication app = builder.Build();

A practical example of the setup

In the code below you can see a real application of the Quartz package within ASP.NET Core MVC.

To better illustrate the use of the Quartz library, imagine you have a Program.cs file that is always created when you choose the MVC architecture, and then imagine a Jobs folder where you have all the tasks you want Quartz to perform in the background when you run your web application.

After that, it's pretty straightforward.

In the Jobs folder, you create a class that will perform the tasks you specify. The class should extend the IJob interface and implement the Execute method.

Example SendEmailJob.cs configuration

public sealed class SendEmailJob : IJob
{
    private readonly IEmailSender sender;

    public SendEmailJob(IEmailSender sender)
    {
        this.sender = sender;
    }

    public ValueTask Execute(IJobExecutionContext context, CancellationToken cancellationToken = default)
    {
        // Code that sends a periodic email to the user (for example)
        return sender.SendDigest(cancellationToken);
    }
}

A job whose work is asynchronous is written async ValueTask as usual. One that only forwards a call, like this one, can return it directly and skip the state machine; one with nothing to await at all returns default, which is a completed ValueTask that allocates nothing. What a job must not do is block: the scheduler is holding a worker slot for it.

After that, you just need to build Quartz trigger in Program.cs, which guarantees that the job will run according to the preset interval.

Example Program.cs configuration

builder.AddQuartz(q =>
{
    // Just use the name of your job that you created in the Jobs folder.
    JobKey jobKey = new("SendEmailJob");
    q.AddJob<SendEmailJob>(opts => opts.WithIdentity(jobKey));

    q.AddTrigger<SendEmailJob>(opts => opts
        .ForJob(jobKey)
        .WithIdentity("SendEmailJob-trigger")
        // This Cron interval can be described as "run every minute" (when second is zero)
        .WithCronSchedule("0 * * ? * *"));
});

builder.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);

For more on cron triggers see the CronTriggers lesson, and for the expression syntax itself the Cron Expression Reference.

Health checks

Quartz registers an ASP.NET Core health check that reports unhealthy when the scheduler is not running or cannot reach its store. Add it alongside your application's other checks:

builder.Services.AddHealthChecks()
    .AddSqlServer(connectionString)
    .AddQuartz();

services.AddQuartzHealthChecks() is the same thing for an application that has no other checks to compose with.

The registration can be customized via the optional configuration callback, for example to attach tags so the check can be filtered into separate liveness and readiness probes:

builder.Services.AddHealthChecks().AddQuartz(options =>
{
    options.Name = "quartz-scheduler";   // the default, or quartz-scheduler-<name> for a named scheduler
    options.Tags.AddRange(["ready", "live"]);
    options.FailureStatus = HealthStatus.Unhealthy;
});

The callback is one source of QuartzHealthCheckOptions among several: the settings go through the options pipeline, so services.Configure<QuartzHealthCheckOptions>(...) and a bound configuration section mean the same thing, whichever order they are written in.

A named scheduler has a check of its own, reporting on its scheduler. Name it on the health checks builder, or ask for one from inside AddQuartz:

builder.Services.AddHealthChecks().AddQuartz("reporting", options => options.Tags.Add("ready"));

// or, where the scheduler is configured
builder.Services.AddQuartz("reporting", q => q.AddQuartzHealthChecks());

Its options are that scheduler's, so they are configured under its name:

builder.Services.Configure<QuartzHealthCheckOptions>("reporting", options => options.Tags.Add("ready"));
app.MapHealthChecks("/healthz/ready", new HealthCheckOptions
{
    Predicate = registration => registration.Tags.Contains("ready")
});

No packages depend on Quartz.AspNetCore.

https://github.com/quartznet/quartznet/releases

.NET 10.0

Version Downloads Last updated
4.0.0-alpha.3 3 08/27/2026
4.0.0-alpha.2 1 08/25/2026
4.0.0-alpha.1 7 08/23/2026
3.20.0 0 08/27/2026
3.19.1 15 07/28/2026
3.19.0 12 07/24/2026
3.18.2 25 06/28/2026
3.18.1 40 04/27/2026
3.18.0 42 04/17/2026
3.17.1 34 04/17/2026
3.17.0 35 04/17/2026
3.16.1 31 04/17/2026
3.16.0 33 04/17/2026
3.15.1 34 04/17/2026
3.15.0 31 04/17/2026
3.14.0 34 04/17/2026
3.13.1 28 04/17/2026
3.13.0 36 04/17/2026
3.12.0 33 04/17/2026
3.11.0 33 04/17/2026
3.10.0 31 04/17/2026
3.9.0 34 04/17/2026
3.8.1 37 04/17/2026
3.8.0 24 04/17/2026
3.7.0 34 04/17/2026
3.6.3 36 04/17/2026
3.6.2 36 04/17/2026
3.6.1 23 04/17/2026
3.6.0 34 04/17/2026
3.5.0 32 04/17/2026
3.4.0 26 04/17/2026
3.3.3 34 04/17/2026
3.3.2 33 04/17/2026
3.3.1 34 04/17/2026
3.3.0 27 04/17/2026
3.2.4 34 04/17/2026
3.2.3 34 04/17/2026
3.2.2 32 04/17/2026
3.2.1 34 04/17/2026
3.2.0 32 04/17/2026
3.1.0 37 04/17/2026
1.0.4 36 04/17/2026
1.0.1 33 04/17/2026
1.0.0 37 04/17/2026