Hangfire vs Quartz.NET vs TickerQ vs Coravel: .NET job schedulers in 2026
An honest comparison of the four in-process .NET job schedulers — persistence, dashboards, clustering, cron support, maintenance status — and when an external scheduler beats all of them.
Every .NET team scheduling background work ends up comparing the same libraries. Here’s the honest version of that comparison — including the option the library docs won’t mention.
The 30-second answer
- Quartz.NET — the most mature and the most capable scheduler as a scheduler: rich cron triggers, calendars, misfire policies, DB-backed clustering. No dashboard, more ceremony.
- Hangfire — the most popular for background jobs in general: fire-and-forget, delayed, and recurring jobs with a great dashboard. Recurring cron is a feature, not the core.
- TickerQ — the newcomer: source-generator based (no reflection), EF Core persistence, a live dashboard, and no polling loop. Modern, but young — check maturity against your risk tolerance.
- Coravel — the simplest: fluent in-process scheduling with zero infrastructure. No persistence — a restart loses state, a single instance is assumed.
- FluentScheduler — historically popular, now effectively in maintenance mode; fine in old codebases, hard to recommend for new ones.
Feature comparison
| Quartz.NET | Hangfire | TickerQ | Coravel | |
|---|---|---|---|---|
| Cron schedules | Yes (6-field, rich) | Yes (recurring jobs) | Yes | Fluent API (Daily(), Cron()) |
| Persistence | ADO.NET stores | SQL/Redis storage | EF Core | None (in-memory) |
| Survives restarts | Yes (with store) | Yes | Yes | No |
| Multi-node clustering | Yes (DB locks) | Yes (storage-based) | Yes | No |
| Dashboard | No (third-party) | Yes | Yes | No |
| Retries | Via listeners/policies | Automatic, configurable | Configurable | Manual |
| How jobs run | Polling + in-memory | Storage polling | In-memory, no polling | In-memory timers |
| Weight | Heavy | Medium | Light-medium | Very light |
Deeper dive on the two incumbents: Quartz.NET vs Hangfire.
What all four share — and can’t fix
They all run inside your application process. That has real consequences:
- Your app must be running. A scheduler inside an app that crashed, is deploying, or scaled to zero schedules nothing. On IIS, app-pool recycling silently pauses everything until the next request warms the site.
- The schedule state lives in your database (or nowhere, for Coravel) — your job table, your polling load, your migrations.
- Nobody outside the process is watching. If the host dies at 01:59 and the backup was due at 02:00, every in-process library fails identically: silently. The scheduler can’t alert you about its own death.
Problems 1 and 2 are architecture trade-offs you might happily accept. Problem 3 is the one worth fixing regardless of which library you pick: pair the schedule with an external heartbeat monitor — the job pings when it completes; a missing ping becomes an email/Slack/Discord/Telegram alert. The .NET SDK wraps this in one attribute.
When to skip the library entirely
If a job boils down to “call this endpoint on a schedule” — cleanups, report generation, cache warming, webhook dispatch — an in-process scheduler is infrastructure you don’t need. Expose the work as an HTTP endpoint and let an external scheduler call it with retries, timeouts, and an execution log:
jobs:
- id: nightly-report
name: Nightly report
kind: http
method: POST
url: https://app.example.com/internal/reports/nightly
schedule: "0 2 * * *"
timezone: Europe/Berlin
retries: 3
headers:
Authorization: Bearer ${CRON_SECRET}
No job tables, no polling load, no dashboard to host — and the schedule survives your deploys. The full trade-off against Hangfire specifically: Hangfire alternative.
Recommendations
- Complex scheduling logic (calendars, misfire handling, chained triggers): Quartz.NET.
- General background-job system with enqueue/retry/dashboard: Hangfire.
- Greenfield, EF Core shop, want a dashboard without Hangfire’s weight: evaluate TickerQ.
- Small app, single instance, jobs that can miss a beat: Coravel.
- “Call this URL on a schedule” jobs, or anything that must not fail silently: externalize the trigger, keep the handler — whatever else you run.