# Hangfire Monitoring: Job Status, Health Checks, and Alerts

By Dan Ochoa · Published 2025-12-03 · Updated 2026-10-31
Canonical: https://cronradar.com/blog/hangfire-monitoring

> Use the Hangfire monitoring API to check job status, inspect recurring jobs, and diagnose failures. Learn what health checks miss and how to add alerts.

**To monitor Hangfire, start with its dashboard and `JobStorage.Current.GetMonitoringApi()`.** They show queues, processing jobs, failures, and stored execution details. For recurring jobs, also inspect the recurring-job registry: an execution can be healthy while tomorrow's registration is missing. Add a schedule-based alert when you need to know that an expected run never arrived.

This guide covers Hangfire 1.8. The examples read your existing Hangfire storage; they do not enqueue, retry, or delete work.

## What should you check first?

| What you see                                 | Check in Hangfire                                         | What it tells you                                         |
| -------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------- |
| A recurring job did not start                | Recurring registration, next execution, server, and queue | Whether the job is registered and a worker can receive it |
| A job is processing for too long             | Processing state, worker logs, and downstream timeouts    | Where the current execution may be waiting                |
| A job failed                                 | Job details, exception, and retry policy                  | Whether it will retry or needs intervention               |
| The worker is online but a report is missing | Last successful execution and the report itself           | Whether the expected business work finished               |
| A job vanished after a deployment            | Recurring-job IDs and registration code                   | Whether the deployment changed or removed the schedule    |

Hangfire's recurring job scheduler checks registrations every `SchedulePollingInterval`, 15 seconds by default in Hangfire 1.8, and enqueues individual background jobs when they are due ([BackgroundJobServer.cs](https://github.com/HangfireIO/Hangfire/blob/v1.8.25/src/Hangfire.Core/BackgroundJobServer.cs)). Its server needs to stay running for that to happen. A web application that is put to sleep can therefore miss scheduling work even when the registration remains in storage. See [Hangfire's recurring-task documentation](https://docs.hangfire.io/en/latest/background-methods/performing-recurrent-tasks.html), and [Hangfire recurring job not running](https://cronradar.com/blog/hangfire-recurring-job-not-running) for a full diagnosis.

## Use the Hangfire monitoring API

`IMonitoringApi` provides a read-only view of stored jobs and statistics. Run this inside an application where Hangfire has already configured `JobStorage.Current`:

```csharp
using Hangfire;

var monitoring = JobStorage.Current.GetMonitoringApi();
var totals = monitoring.GetStatistics();

Console.WriteLine($"Processing: {totals.Processing}");
Console.WriteLine($"Failed: {totals.Failed}");
Console.WriteLine($"Succeeded: {totals.Succeeded}");

// Read one bounded page, not the entire failed-job history.
foreach (var entry in monitoring.FailedJobs(0, 20))
{
    Console.WriteLine($"Failed background job: {entry.Key}");
}
```

The counts describe stored state. A failed-job count is not an error rate: old failed jobs can remain after you have dealt with them, and retention affects successful-job history. Compare observations over a defined period if you want trends.

Use pagination for lists and keep diagnostic access authenticated. Hangfire job details may include method arguments and exception information; do not expose them through a public health endpoint. The dashboard needs the same protection: by default it serves only local requests, and [Hangfire dashboard authorization](https://cronradar.com/blog/hangfire-dashboard-authorization) covers filters, policies, and read-only access for production. The [IMonitoringApi reference](https://api.hangfire.io/html/T_Hangfire_Storage_IMonitoringApi.htm) lists the available queries.

### Get a job's current status and details

Use a **background-job ID** from the dashboard for an individual execution:

```csharp
using Hangfire;

var backgroundJobId = "12345"; // Replace with an existing execution ID.
var monitoring = JobStorage.Current.GetMonitoringApi();

using var connection = JobStorage.Current.GetConnection();
var state = connection.GetStateData(backgroundJobId);
var details = monitoring.JobDetails(backgroundJobId);

Console.WriteLine($"Current state: {state?.Name ?? "not found"}");
Console.WriteLine($"Stored details available: {details is not null}");
```

`GetStateData` reads the current state. `JobDetails` retrieves the stored job details and state history; it can return null when the job no longer exists. You do not need to guess the ordering of its history to find the current status.

These are separate reads, so a busy worker may change state between them. Treat the output as a diagnostic snapshot, not a transactionally consistent decision to retry a job. Reference: [JobDetails](https://api.hangfire.io/html/M_Hangfire_Storage_IMonitoringApi_JobDetails.htm) and [IStorageConnection](https://api.hangfire.io/html/T_Hangfire_Storage_IStorageConnection.htm).

### Recurring-job ID versus background-job ID

A recurring registration such as `nightly-backup` defines a schedule. Each scheduled execution gets its own background-job ID. Passing `nightly-backup` to `JobDetails` does not inspect the recurring definition.

Read the registry instead:

```csharp
using Hangfire;
using Hangfire.Storage;

using var connection = JobStorage.Current.GetConnection();

foreach (var recurring in connection.GetRecurringJobs())
{
    Console.WriteLine(
        $"{recurring.Id}: cron={recurring.Cron}, " +
        $"next={recurring.NextExecution}, " +
        $"last background job={recurring.LastJobId}");
}
```

Use `LastJobId` to investigate the most recently scheduled execution. It does not prove that execution succeeded. If the registration is absent, check whether startup registered it, whether its ID changed, and whether a deployment called `RemoveIfExists`. For a large registry, avoid running this full enumeration on every health probe.

## A Hangfire health check does not prove that a job finished

A useful worker health check answers a narrow question: can this process reach storage, and is the worker available? A recurring-job check asks whether a particular run completed by its deadline. Keep those results distinct.

For example, a worker can be healthy while a customer import repeatedly fails on an expired credential. Conversely, one old failed job can remain in storage while current work runs normally.

Use worker health for availability and readiness decisions. Use job state, schedule expectations, and alerts to investigate failed or missing work. The same distinction applies to any scheduler; see [how to know if a cron job ran](https://cronradar.com/blog/how-to-know-if-cron-job-ran). **Do not restart a worker merely because the historical failed-job count is above zero.** A restart does not repair the old exception and can interrupt unrelated jobs.

If you add a health-check package, check which condition it actually measures. A storage connection check, a server-heartbeat check, and a count of failed jobs are three different checks. Protect detailed responses and keep public health output minimal.

## Handle retries before choosing when to alert

Hangfire records thrown exceptions and applies its retry policy. The default `AutomaticRetryAttribute` schedules up to ten retry attempts; your global filters or job attributes can change that. Once attempts are exhausted, the job normally remains in the Failed state for investigation. See [Hangfire's exception and retry documentation](https://docs.hangfire.io/en/latest/background-processing/dealing-with-exceptions.html).

Decide whether an alert means:

- An attempt failed but Hangfire may recover through a retry.
- The execution exhausted its retries.
- The recurring job failed to complete before its deadline.

Those conditions can happen at different times. For a nightly backup, missing the completion deadline may matter even while a retry is waiting. For a frequent cache refresh, alerting on every temporary attempt failure may create unnecessary noise.

Keep the original exception and let Hangfire apply the configured policy. Avoid swallowing job exceptions just to make a monitor look healthy, and avoid an unawaited HTTP request in a filter that may be lost during shutdown. [Hangfire automatic retry](https://cronradar.com/blog/hangfire-automatic-retry) covers the retry settings and a filter that detects the final failure.

## Add independent alerts for recurring jobs

CronRadar watches an expected schedule and receives execution reports from your application. It can alert when an expected run is missing, a reported execution fails, or a run that started has no completion by the next expected run plus its grace period. This is the [dead man's switch](https://cronradar.com/blog/cron-dead-mans-switch) pattern applied to Hangfire: the alert comes from the absence of an expected report, so it still fires when the Hangfire server itself is down.

For Hangfire, start with the [current Hangfire integration guide](https://docs.cronradar.com/integrations/hangfire). Create a CronRadar application for the environment you want to monitor and store its key as `CRONRADAR_API_KEY` in your deployment's secret configuration.

Install the package in the host that runs Hangfire:

```bash
dotnet add package CronRadar.Hangfire
```

After your existing Hangfire service registration, add:

```csharp
using CronRadar.Hangfire;

// Keep your existing AddHangfire(...) and AddHangfireServer() setup.
builder.Services.AddCronRadarHangfire();
```

This discovers recurring registrations without adding monitoring calls to every job method, and `[SkipMonitor]` on a job method opts it out. Alerts go to email, Slack, webhook, Discord, or PagerDuty. Keep the Hangfire dashboard for queues, retries, worker administration, and detailed debugging.

**Package status checked September 20, 2026:** the published `CronRadar.Hangfire 0.1.0` supports setup and discovery, but can report failures before Hangfire finishes retrying, including cancellations. Reporting only after retries are exhausted requires the pending 0.2.1 release. Check the integration guide's current release notice before choosing an alert policy; do not assume repository documentation means a package has already shipped.

If the same application also schedules work with Quartz.NET, the [Quartz.NET monitoring guide](https://cronradar.com/blog/quartz-net-monitoring) covers its listeners and the CronRadar.Quartz package.

If an assistant is making the change, use the [agent quickstart](https://docs.cronradar.com/agent-quickstart). You choose the workspace, permissions, and expiry for its temporary access. The deployed job uses its own application key.

## Verify one real recurring run

Choose a job whose output you can safely inspect, then verify:

1. **Registration:** the expected job and schedule appear in the correct CronRadar application. Keep development and production in separate applications.
2. **Execution:** let the actual Hangfire job run. Confirm its start and completion correspond to that execution.
3. **Result:** check the backup, report, or imported data itself. A successful method return does not establish that the intended output is useful.
4. **Notification:** send a test from alert settings and confirm the destination receives it. Test missed-run or failure behavior with a dedicated test job.
5. **Next schedule:** confirm a subsequent scheduled execution arrives. A one-off manual report does not verify the deployed scheduler.

Keep existing monitoring active while checking the new path. See [Hangfire setup and coverage](https://cronradar.com/hangfire) for the integration overview and [verification steps](https://docs.cronradar.com/verify) for a fuller checklist.

## Frequently Asked Questions

### Does Hangfire have a monitoring API?

Yes. `JobStorage.Current.GetMonitoringApi()` returns `IMonitoringApi`, with methods including `GetStatistics`, `FailedJobs`, and `JobDetails`. It reads stored execution information; the recurring registry is read through a storage connection.

### How do I check whether a Hangfire job succeeded?

Find the background-job ID for that execution and read its current state, or inspect it in the dashboard. For a recurring job, first find the relevant execution from its registration or history. Check the actual output too when success means more than a method returning normally.

### Why is my recurring job registered but not running?

Check the Hangfire server, application lifetime, queue configuration, next execution time, and deployment registration code. The recurring entry is a schedule definition; it still needs a running server and a worker able to process its queue.

### Can I replace the Hangfire dashboard with CronRadar?

Keep it. Hangfire's dashboard manages execution and troubleshooting. CronRadar adds schedule-based alerts and a view of recurring work across applications.

### Does CronRadar alert on every Hangfire retry attempt?

That depends on the package version. The repository's 0.2.1 release reports a failure only after Hangfire exhausts its automatic retries. The published CronRadar.Hangfire 0.1.0 (checked September 20, 2026) can report a failure before retries finish, including cancellations, so an alert can arrive for a job that later succeeds on retry. The [integration guide](https://docs.cronradar.com/integrations/hangfire) carries the current release notice.
