Quartz.NET 4.1: a .NET 10 job scheduler with a persistent store
Quartz Enterprise Scheduler .NET
At a glance
- What is it?
- Quartz.NET is the .NET port of the Quartz Enterprise Scheduler, and version 4 targets .NET 10 only. This review covers what it does, how the scheduler and job store fit together, and where it is the wrong choice.
- Who is it for?
- Adopt Quartz.NET when you need cron-style recurring jobs with misfire handling and a persistent store, and when you are already on .NET 10 or can move to it. Stay away if you want a fire-and-forget background queue, since BackgroundService covers that with no extra dependency, or if you cannot leave .NET Standard 2.0 and .NET Framework 4.6.2, where the 3.x branch is the only option.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Quartz.NET solves, and for whom
Quartz.NET is a job scheduler: a library that decides when work runs, then runs it. That is a different job from a message queue, which decides where work goes. The distinction matters because scheduling has its own hard problems, and they are the ones Quartz.NET spends its code on. What happens when a trigger should have fired at 02:00 but the process was down? What happens when a job runs longer than its own interval? What happens when two machines in the same cluster both decide the 02:00 job is theirs?
The audience is .NET teams that already know they need those answers. A nightly invoice reconciliation, a report generated every weekday at 06:00, a cache warm-up every five minutes: these are cron-shaped tasks, and the README frames the project as an enterprise scheduler rather than a general background worker. The package name on NuGet is Quartz, and the README states that dependency injection, hosting, the health check and System.Text.Json serialization are all inside that single package. Teams that expected to assemble four or five NuGet packages get one.
The compatibility line is unusually blunt, and it is the first decision a reader has to make. Quartz.NET 4 targets .NET 10, and nothing else. If your application is on .NET 8 or .NET Framework, version 4 is not a candidate at all. The README points to the 3.x branch, which supports .NET Standard 2.0 and .NET Framework 4.6.2 and later. Two supported lines, two different target sets, and the project ships releases on both: v4.1.1 and v3.22.0 landed within days of each other in September 2026.
Scheduler, triggers and jobs: the actual data flow
The model is three objects. A job is the unit of work. A trigger decides when the job fires. A scheduler owns both and executes the job when its trigger says so. Everything else in the API is configuration around those three.
Triggers are where the scheduling logic lives. A simple trigger fires on an interval or a repeat count. A cron trigger takes a cron expression, which is what makes the "every weekday at 06:00" case a one-line declaration instead of hand-written date arithmetic. Cron is listed among the repository topics next to job-scheduler and scheduled-tasks, and it is the feature most people arrive for.
The part that separates Quartz.NET from a timer is the job store. The scheduler does not have to keep its state in memory. A persistent store records jobs, triggers and their fire times, so the schedule survives a process restart and, in a clustered deployment, is shared between nodes. That is also where misfire handling becomes meaningful: when the scheduler comes back after downtime, a stored trigger has a recorded fire time in the past, and the policy attached to that trigger decides whether the missed execution is skipped, fired once immediately, or replayed. The README does not document those policies; they live in the API reference at /apidoc/4.x, which the README says is generated from the XML documentation comments of the shipped packages and rolls forward with every 4.x minor release.
One consequence of a shared store is worth stating plainly: if you run multiple scheduler instances against the same store, the store is now a coordination point and a piece of infrastructure you operate. The database/ directory at the top level of the repository exists for that reason, and the README does not walk through the schema or the supported database list. Anyone planning a cluster should treat the store choice as a design decision, not a configuration detail.
Installing Quartz.NET and scheduling a first job
The install is one command. The README gives it exactly as shown, and notes that dependency injection, hosting, the health check and System.Text.Json serialization are included, so there is no second package to add for a basic host.
dotnet add package QuartzAfter that, a first scheduler is a few lines: build a scheduler from a factory, create a job, attach a trigger, start. The README does not inline a code sample; it sends readers to the quick start page at https://www.quartz-scheduler.net/documentation/quartz-4.x/quick-start.html, which the README says also lists the optional packages and what each one is for. That page, not the README, is where the first working program comes from.
For a first real use, pick the smallest recurring task you own and give it a cron trigger. The repository's own build tooling is a useful reference point for how the project expects .NET work to be driven from a script rather than from an IDE:
./build.shOn Windows the equivalent is `build.cmd`. Both scripts restore the Fallout CLI from `.config/dotnet-tools.json` and hand it their arguments, so nothing has to be installed globally. `build.cmd Compile UnitTest` compiles the code and runs the unit tests; the README states that the integration tests need a running Docker daemon and that CONTRIBUTING.md has the rest. If you are evaluating Quartz.NET inside an existing application rather than a new one, the same pattern applies: add the package, register the scheduler with the host, and let the host own its lifetime.
Where Quartz.NET is the wrong tool
The most common mismatch is reaching for a scheduler when the problem is a queue. If a request arrives and you want work done off the request thread, with retries and no calendar involved, Quartz.NET adds a trigger model, a job store and a scheduler lifecycle that you do not need. A hosted background service is the smaller answer, and the search data shows people comparing the two directly.
The second mismatch is version. Quartz.NET 4 targets .NET 10, and nothing else. That sentence in the README is a hard constraint, not a preference. A team on .NET 8 cannot adopt 4.x without moving the runtime first, and a team on .NET Framework 4.6.2 stays on the 3.x branch. Running two supported lines means bug fixes may land in one and not the other, and the documentation is split accordingly: the README says the 3.x API reference stays at /apidoc/3.0 while the 4.x set rolls forward with each minor release. When you read a page, check which version it describes.
The third is operational weight. A persistent job store is a database. Clustering adds coordination on top of it. If your jobs are idempotent, short and tolerant of a missed run, an in-memory scheduler or a plain timer is less machinery to own. Quartz.NET earns its cost when missed executions and duplicate executions are the failure modes you actually care about.
How Quartz.NET differs from Hangfire, Coravel and TickerQ
The alternatives that come up most often for .NET are Hangfire, Coravel, TickerQ and FluentScheduler, and the split is about where the schedule lives.
Hangfire is the closest comparison by capability and the furthest by design. It is built around a persistent store too, but its model is queue-centric: you enqueue a job and a worker picks it up, with a dashboard for inspecting and retrying failed work. Quartz.NET is trigger-centric: you declare when something fires, and the interesting machinery is in the trigger, the cron expression and the misfire policy. If your mental model is "run this at 06:00 every weekday", Quartz.NET maps directly. If it is "process this item, retry it three times, show me the failures", Hangfire's model is closer to the problem.
Coravel and FluentScheduler sit at the other end. They are in-process schedulers with a fluent configuration API and no external store, which makes them quick to adopt and unsuitable for the case where two application instances must not both run the same nightly job. TickerQ appears in the same comparison set. The README does not benchmark or compare any of these, so the honest framing is architectural: Quartz.NET is the option that brings a store, a cluster and misfire semantics, and you pay for them in configuration and infrastructure.
One practical note on the repository itself: the docs site is a VuePress 2 project, with `docs:dev` and `docs:build` scripts in package.json and a `docs:lint` script running markdownlint-cli2 over `docs/**/*.md`. That tells you where the documentation lives and how it is checked, which is useful if you find a gap and want to file a fix rather than a complaint.
Maintenance, licensing and upgrade cost
The repository is not archived, and the last push was on 2026-09-20, two days before this review. Releases are frequent and the two lines move in parallel: v4.1.1 on 2026-09-19, v4.1.0 and v3.22.0 both on 2026-09-13. Preview builds of unreleased `main` are pushed on every commit to the Feedz.io feed at https://f.feedz.io/quartznet/quartznet/nuget/index.json, which the README offers as the way to try work that has not shipped.
The licence is Apache-2.0, stated in the README and in the license.txt file at the repository root. Apache-2.0 is permissive and includes an explicit patent grant, and the README's own wording is the standard notice that the software is distributed without warranties or conditions. That is a summary of what the files say, not legal advice; if the patent or notice clauses matter to your organisation, read the licence text with whoever handles that.
The upgrade cost is dominated by the target framework, not the API. Moving from 3.x to 4.x means moving to .NET 10, because 4 targets nothing else. The README does not document a migration guide or a rollback path, and it does not describe breaking changes between the lines. That absence is the thing to plan around: before upgrading, confirm which optional packages your application actually uses by checking the quick start page, and check the 4.x API reference for the types you depend on. If you cannot move the runtime, the 3.x branch is where you stay, and the version split in the documentation means every page needs a version check before you trust it.
Editorial conclusion
Adopt Quartz.NET when you need cron-style recurring jobs with misfire handling and a persistent store, and when you are already on .NET 10 or can move to it. Stay away if you want a fire-and-forget background queue, since BackgroundService covers that with no extra dependency, or if you cannot leave .NET Standard 2.0 and .NET Framework 4.6.2, where the 3.x branch is the only option. Before committing, verify the exact package list for your scenario on the 4.x quick start page, decide which job store you will run, and confirm whether a single node or a clustered deployment is what your workload actually needs.
Frequently asked questions
Is Quartz.NET a Java scheduler?
No. Quartz.NET is a .NET scheduler, described in the README as the Quartz Enterprise Scheduler .NET, and it is installed as the NuGet package Quartz. The Java origin is visible in the naming and in the cron-style trigger model, but the code you write is C#.
How can I use Quartz.NET in C#?
Add the package with dotnet add package Quartz, which the README says includes dependency injection, hosting, the health check and System.Text.Json serialization. The README then points to the 4.x quick start page for the first working example rather than inlining one.
What is Quartz.NET used for?
It schedules recurring work: a job is the unit of work, a trigger decides when it fires, and the scheduler runs it. Cron expressions cover calendar-style schedules, and a persistent job store lets the schedule survive a restart or be shared across nodes.
What is the difference between Quartz.NET and Hangfire?
Quartz.NET is trigger-centric: you declare when a job fires and attach a misfire policy for when a fire time is missed. Hangfire is queue-centric, built around enqueuing jobs that workers pick up, with a dashboard for inspecting and retrying failures. The README does not compare the two.
Why use Quartz.NET instead of a background service?
A background service runs work off the request thread but has no calendar, no persistent store and no misfire policy. Quartz.NET adds a trigger model and a job store, so a schedule survives a restart and, when several instances share the store, only one node runs the job.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/quartznet-quartznet)