# Sentry Crons vs a Dedicated Cron Monitor: Which Fits Your Jobs

By Dan Ochoa · Published 2026-10-25 · Updated 2026-10-31
Canonical: https://cronradar.com/blog/sentry-crons-vs-dedicated-cron-monitoring

> Sentry Crons compared with a dedicated cron monitor: check-ins, time zones, max runtime, SDK coverage, alerts, and pricing, and when each is the better fit.

**Sentry Crons fits when your team already works in Sentry and wants a missed or failed job to appear as an issue next to the errors and traces that explain it. A dedicated cron monitor fits when you want job monitoring kept apart from your error tracker, pricing and alerts built around schedules, or, with CronRadar, automatic discovery of Hangfire and Quartz.NET recurring jobs.** Both work the same way underneath: the job checks in when it starts and finishes, and the service alerts when a check-in is late, reports failure, or never closes. The differences are in schedule options, how much of your scheduler each SDK instruments, where alerts can go, and what a monitor costs. Every Sentry fact below comes from Sentry's own documentation and pricing page, checked October 25, 2026.

## How Sentry Crons Works

Sentry's product docs call the feature Cron Monitoring; its SDK pages call it Sentry Crons. A cron monitor belongs to a Sentry project and receives check-ins with one of three statuses: `in_progress` when the job starts, then `ok` or `error` when it ends ([HTTP setup](https://docs.sentry.io/product/monitors-and-alerts/monitors/crons/getting-started/http/)). A job that doesn't start on time produces a missed check-in. A job that reports `error`, or stays in progress past its maximum runtime, fails. A single `ok` or `error` with no `in_progress` before it is a heartbeat, which detects missed runs but can't detect a run that overruns.

### Monitor Settings

You can configure a monitor in the Sentry UI, or send the configuration from code as `monitor_config` on a check-in, which creates the monitor or updates the existing one. The keys, as Sentry's [Python Crons page](https://docs.sentry.io/platforms/python/crons/) lists them:

| Key                       | What it controls                                                                                                                                                                                                        |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `schedule`                | Five-field crontab, or an interval from minutes to years; one minute at the shortest, no seconds or year fields ([troubleshooting](https://docs.sentry.io/product/monitors-and-alerts/monitors/crons/troubleshooting/)) |
| `timezone`                | A tz database name such as `Europe/Vienna`                                                                                                                                                                              |
| `checkin_margin`          | Minutes to wait before a check-in counts as missed                                                                                                                                                                      |
| `max_runtime`             | Minutes a run may stay in progress before it fails, up to 10,080 (7 days)                                                                                                                                               |
| `failure_issue_threshold` | Consecutive failed check-ins before Sentry creates an issue                                                                                                                                                             |
| `recovery_threshold`      | Consecutive OK check-ins before the issue resolves                                                                                                                                                                      |
| `owner`                   | The team or user that new issues from this monitor are assigned to                                                                                                                                                      |

A check-in can also name an `environment` (`production` by default), giving one monitor a separate status per environment, and overlapping runs need a stable `check_in_id` per run. Sentry accepts six check-ins per minute per monitor environment and keeps them for 90 days.

### Which Schedulers Sentry's SDKs Instrument

Sentry's [setup page](https://docs.sentry.io/product/monitors-and-alerts/monitors/crons/getting-started/) lists Crons support in its PHP, Python, Node, Go, Java, .NET, Ruby, and Elixir SDKs. Some integrations cover every periodic job of a scheduler once you turn them on:

- **Python:** Celery beat with `monitor_beat_tasks=True`, on the default beat scheduler or RedBeat ([Celery Crons](https://docs.sentry.io/platforms/python/integrations/celery/crons/)); arq, Huey, and RQ with rq-scheduler from `sentry-sdk` 2.73.0 ([Python Crons](https://docs.sentry.io/platforms/python/crons/)).
- **Ruby:** sidekiq-cron and sidekiq-scheduler ([Ruby Crons](https://docs.sentry.io/platforms/ruby/crons/)); [Sidekiq monitoring](https://cronradar.com/blog/sidekiq-monitoring) covers queue latency, retries, and dead jobs.
- **Elixir:** Oban and Quantum ([Elixir Crons](https://docs.sentry.io/platforms/elixir/crons/)).
- **Next.js on Vercel:** Vercel Cron Jobs, read from `vercel.json` ([Next.js Crons](https://docs.sentry.io/platforms/javascript/guides/nextjs/crons/)).

Others need a marker or wrapper on each job: Laravel's `->sentryMonitor()` on a scheduled task ([Laravel Crons](https://docs.sentry.io/platforms/php/guides/laravel/crons/)), wrappers for the `cron`, `node-cron`, and `node-schedule` packages that name each job ([Node.js Crons](https://docs.sentry.io/platforms/javascript/guides/node/crons/)), NestJS's `@SentryCron` ([NestJS Crons](https://docs.sentry.io/platforms/javascript/guides/nestjs/crons/)), Go's `sentrycron.AddFunc` for robfig/cron ([Go Crons](https://docs.sentry.io/platforms/go/crons/)), Spring's `@SentryCheckIn` ([Spring Boot Crons](https://docs.sentry.io/platforms/java/guides/spring-boot/crons/)), a `SentryJobListener` for Java's Quartz ([Quartz integration](https://docs.sentry.io/platforms/java/integrations/quartz/)), and `[SentryMonitorSlug]` for Hangfire in .NET.

Check the documented limits against your setup: a Celery beat monitor is created the first time its task runs, only for crontab-parsable schedules, and Spring's `@SentryCheckIn` sends no monitor configuration, so that monitor must exist in Sentry first.

### Without the SDK: HTTP and sentry-cli

Any job can send check-ins over HTTP to an ingest URL built from your organization, project, monitor slug, and the project's public key, with `?status=in_progress`, `?status=ok`, or `?status=error`. For a command run by cron, `sentry-cli monitors run` (version 2.16.1 or later, authenticated with `SENTRY_DSN`) wraps the command and can create the monitor on its first run ([Crons CLI](https://docs.sentry.io/cli/crons/)):

```bash
#!/bin/sh
# Run by cron at 02:30 server time (Europe/Berlin). SENTRY_DSN is set in the job's environment.
exec sentry-cli monitors run \
  --schedule "30 2 * * *" --timezone "Europe/Berlin" \
  --check-in-margin 15 --max-runtime 90 \
  nightly-backup -- /opt/backup.sh
```

In sentry-cli 3.8.2 the command sends `in_progress`, runs the job, sends `ok` or `error` by exit status with the run's duration, and exits with the job's own exit code. A check-in that fails to send is logged, and the job still runs ([run.rs](https://github.com/getsentry/sentry-cli/blob/3.8.2/src/commands/monitors/run.rs)).

### How Sentry Alerts on a Monitor

A missed or failed check-in creates an error event tagged with the monitor's slug, and that event creates or updates an issue ([job monitoring](https://docs.sentry.io/product/monitors-and-alerts/monitors/crons/job-monitoring/)). With the Sentry SDK in the job, errors thrown during the run appear on the monitor's page too. Notifications come from Alerts, rules with triggers, filters such as `monitor.slug`, and actions; if a failing monitor sends nothing, Sentry's troubleshooting page says to check that alerts are set up. Depending on installed integrations, actions can notify people through their Sentry notification settings, post to Slack, Discord, or Microsoft Teams, page through PagerDuty or Opsgenie, call a webhook, or open a Jira, GitHub, or Azure DevOps ticket ([Creating an Alert](https://docs.sentry.io/product/monitors-and-alerts/alerts/create-alerts/)).

A monitor environment with only missed, timed-out, or failed check-ins for 14 days is marked broken, and Sentry emails its owner, or the project team when it has none. After 14 more days Sentry mutes it, and muted monitors are still billed.

### What Sentry Crons Costs

From [Sentry's pricing page](https://sentry.io/pricing/), checked October 25, 2026: every plan includes one cron monitor. The free Developer plan is limited to one user and lists alerts and notifications by email. Team is $26 a month and Business $80 a month, both quoted "when billed annually with default pre-paid data", and each additional cron monitor costs $0.78. Additional monitors come only from a pay-as-you-go budget, which is available only on paid plans ([Manage Your Cron and Uptime Monitors](https://docs.sentry.io/pricing/quotas/manage-cron-monitors/)).

Sentry bills pay-as-you-go usage monthly, and the budget is shared across all data categories on a first-come, first-served basis ([Pricing & Billing](https://docs.sentry.io/pricing/)). At the start of each billing period, monitors that the budget can't cover may be deactivated, so size the budget for your monitor count.

## How CronRadar Handles the Same Jobs

CronRadar is a dedicated monitor for scheduled jobs. The model is the same, with different trade-offs:

- **Check-ins:** `start`, `complete`, and `fail`, or a single heartbeat, over HTTPS to `https://cron.life`. A `run_id` pairs a run's events. A heartbeat detects missed runs but not duration or stuck runs.
- **Schedules:** standard five-field cron, evaluated in UTC. There's no time-zone setting and no interval schedule. A job on a daylight-saving zone needs the earlier of its two UTC times and a grace period that covers the hour it shifts.
- **Grace:** `gracePeriod` in seconds, minimum 60, default 90. A check-in's `schedule` and `gracePeriod` apply only when that check-in creates the monitor; change them later in the monitor's settings. [Choosing a grace period](https://cronradar.com/blog/cron-grace-period) covers sizing it.
- **Failures:** a `fail` opens an incident immediately; there are no consecutive-failure or recovery-count settings. CronRadar sends the first alert, at most one reminder every 24 hours until you acknowledge it, and a recovery message when a successful run resolves it.
- **Stuck runs:** there is no maximum-runtime setting. Every check-in, `start` included, moves the expected time to the job's next scheduled run. If that time plus grace passes while the last event is a `start` with no finish, the monitor goes `critical` as stuck. A daily job that hangs is flagged about a day later, and if the next run's `start` arrives first, it resets the window. Put a timeout inside the job so a hang becomes a `fail`; [cron job timeouts](https://cronradar.com/blog/cron-job-timeout) shows how.
- **Outcome checks:** a `complete` sent by POST can carry up to five numeric or boolean values, such as `{"outcomes":{"records_processed":842}}`. Once expectations are configured in the job's settings, a missing or violated value opens an incident. That catches the [run that exits 0 and does nothing](https://cronradar.com/blog/scheduled-job-silent-success). Sentry's docs list cron monitor settings as schedule, check-in margin, max runtime, and failure tolerance, and describe Metric Monitors for thresholds on application metrics ([Monitors](https://docs.sentry.io/product/monitors-and-alerts/monitors/)).
- **Operational context:** each monitor can carry an owner or team, severity, environment, alert destinations, and logs, runbook, and repository URLs. The owner appears in every alert, and severity in Slack, Discord, PagerDuty, and webhook alerts. The URLs go into webhook payloads and PagerDuty custom details. CronRadar collects no logs, traces, or job payloads, and a `fail` message is limited to 1,000 characters, so stack traces stay in your error tracker.
- **Alert channels:** five: email, Slack, webhook, Discord, and PagerDuty.
- **Pricing:** Free covers up to 10 active monitors with every alert channel and no card. On Grow, the first 10 active monitors stay free, and each further monitor is billed monthly by schedule class: $2 Fast (gap under 5 minutes), $1 Frequent (5 minutes to under an hour), $0.50 Standard (hourly or less often). Billing is in arrears on the month's peak, with no tiers, minimum, or monitor limit. An optional monthly budget holds new monitors paused, and existing monitors keep running. Business is for custom requirements. Runs, retries, and API calls are never billed.

The same Berlin backup, reported with `curl` alone, needs its schedule in UTC: 02:30 local is 00:30 UTC in summer and 01:30 in winter, so the schedule uses the earlier time and a 90-minute grace absorbs the winter hour. The cost is that in summer a missed run is flagged about 90 minutes after its scheduled time, where the Sentry example's margin is 15:

```bash
#!/bin/sh
# /opt/backup-monitored.sh, run by cron at 02:30 Europe/Berlin
url="https://cron.life/api/v1/ping/nightly-backup"
run_id="nightly-backup-$(date +%s)-$$"

report() {
  event=$1
  shift
  curl --fail --silent --show-error --max-time 5 --output /dev/null \
    --request POST --get --user "$CRONRADAR_API_KEY:" \
    --data-urlencode "schedule=30 0 * * *" \
    --data-urlencode "gracePeriod=5400" \
    --data-urlencode "run_id=$run_id" \
    "$@" "$url/$event" || true
}

report start
/opt/backup.sh
rc=$?
if [ "$rc" -eq 0 ]; then
  report complete
else
  report fail --data-urlencode "message=backup.sh exited with code $rc"
fi
exit "$rc"
```

Each report gives up after five seconds and ignores errors, and the script exits with the backup's own code. Because every report carries the schedule, the first run creates the monitor and a deleted one is recreated.

CronRadar's installable SDKs today are its .NET packages. Its Node.js, Python, Celery, PHP, and Laravel SDKs aren't installable, so those stacks, like Go, Java, Ruby, and Elixir, report over HTTP as above, each job with its own check-in and no auto-discovery. [Celery monitoring](https://cronradar.com/blog/celery-monitoring) shows that route for beat tasks.

## Hangfire and Quartz.NET: Where the Setups Differ Most

Sentry's .NET SDK monitors Hangfire jobs through a job filter added with `UseSentry()`. To tell it which jobs to monitor, you give each one a slug ([Hangfire Crons](https://docs.sentry.io/platforms/dotnet/crons/hangfire/)):

```csharp
using Hangfire;
using Sentry.Hangfire;

// Assumes the Sentry .NET SDK is already initialized.
builder.Services.AddHangfire(configuration => configuration
    .UseSentry());

public class ReportJobs
{
    [SentryMonitorSlug("nightly-report")]
    public void SendNightlyReport() => Console.WriteLine("Sending nightly report");
}
```

CronRadar.Hangfire works the other way around: one registration, and every recurring job is monitored unless you opt it out ([Hangfire integration](https://docs.cronradar.com/integrations/hangfire)):

```csharp
using CronRadar.Hangfire;

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

public class MaintenanceJobs
{
    [SkipMonitor] // Every recurring job without this attribute is monitored.
    public void RebuildDevelopmentFixtures() => Console.WriteLine("Rebuilding fixtures");
}
```

At startup it reads Hangfire's recurring-job registry, declares each job and its schedule, and reconciles the registry every 15 minutes by default. Because discovery creates monitors ahead of the first run, a discovered job that never runs stays `pending`, and CronRadar sends a never-executed alert once its first expected run plus grace passes. A job that disappears from the registry is paused and marked as no longer discovered, with its history kept. One caveat for alert policy: the published CronRadar.Hangfire 0.1.0 (checked September 20, 2026) can report a failure before Hangfire finishes retrying, including cancellations. Reporting only after retries are exhausted needs the pending 0.2.1 release.

For Quartz.NET 3.x (3.4 and later), CronRadar.Quartz 0.1.0 registers every triggered job with `await scheduler.MonitorAll();` after jobs are registered and before `Start()`, with `[SkipMonitor]` on an `IJob` class to opt out. 0.1.0 converts a repeating simple trigger to an approximate five-field schedule but sends a cron trigger's six-field expression unchanged, which CronRadar rejects without a warning, so cron-triggered jobs aren't monitored until the pending 0.1.1. Sentry documents its Quartz integration for the Java SDK; its [.NET Crons page](https://docs.sentry.io/platforms/dotnet/crons/) covers Hangfire, a `SentrySdk.WithMonitorAsync` wrapper, and manual check-ins.

For plain .NET jobs, and for Quartz.NET 4.x, which CronRadar.Quartz's docs don't cover, both products offer a wrapper. Both rethrow the job's exception after reporting the failure:

```csharp
// Sentry .NET SDK, newer than 6.12.1
await SentrySdk.WithMonitorAsync("nightly-cleanup", async () =>
{
    await RunCleanupAsync();
}, options =>
{
    options.Interval("0 2 * * *");
    options.MaxRuntime = TimeSpan.FromMinutes(60);
});

// CronRadar 1.0.13
// The class shares its namespace's name, so qualify it fully.
await CronRadar.CronRadar.WrapAsync("nightly-cleanup", async () =>
{
    await RunCleanupAsync();
}, schedule: "0 2 * * *");
```

## Sentry Crons and CronRadar Side by Side

Sentry facts checked October 25, 2026, from the pages linked above.

|                           | Sentry Crons                                                                                                            | CronRadar                                                        |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| Check-in events           | `in_progress`, `ok`, `error`, or heartbeat                                                                              | `start`, `complete`, `fail`, or heartbeat                        |
| Schedule types            | Five-field crontab or interval                                                                                          | Five-field cron only                                             |
| Time zone                 | Per monitor                                                                                                             | UTC only                                                         |
| Late tolerance            | `checkin_margin`, minutes                                                                                               | `gracePeriod`, seconds (minimum 60, default 90)                  |
| Overrunning run           | `max_runtime`, up to 7 days                                                                                             | No max-runtime setting; stuck window per job                     |
| Alert thresholds          | `failure_issue_threshold`, `recovery_threshold`                                                                         | First `fail` alerts; next success recovers                       |
| Values the job reports    | Separate Metric Monitor on an application metric                                                                        | Up to five outcome values on `complete`                          |
| Automatic instrumentation | Celery beat, arq, Huey, RQ, sidekiq-cron, sidekiq-scheduler, Oban, Quantum, Vercel Cron Jobs; per-job markers elsewhere | Hangfire discovery; Quartz.NET 3.x simple-trigger discovery      |
| Alert destinations        | Email, Slack, Discord, Teams, PagerDuty, Opsgenie, webhooks, and issue trackers via integrations                        | Email, Slack, webhook, Discord, PagerDuty                        |
| History                   | 90 days of check-ins                                                                                                    | 30 days of runs on Free, 90 on Grow                              |
| Monitors included         | 1 on every plan, then $0.78 each on paid plans                                                                          | 10 free, then on Grow $2, $1, or $0.50 a month by schedule class |

For 25 jobs on a paid Sentry plan you already have, the cron monitor charge is 24 × $0.78 = $18.72 a month; Sentry notes its listed per-category prices are rounded. CronRadar charges for 15 of the 25: $7.50 if all are hourly or slower, $15 if they run every 5 to 59 minutes, and $30 if they run more often than every 5 minutes. The Sentry figure excludes the plan fee itself.

## When Sentry Crons Fits Better

- **You already pay for Sentry.** Errors, traces, and check-ins share one project, a failed run's exception appears on the monitor's page, and each additional monitor costs $0.78.
- **Your schedules need a time zone, an interval, a per-run `max_runtime`, or a consecutive-failure threshold,** none of which CronRadar has.
- **Sentry instruments your scheduler for you,** as with Celery beat, sidekiq-cron, Oban, or Vercel Cron Jobs, where CronRadar has no installable SDK today.
- **You route alerts to Microsoft Teams, Opsgenie, or a ticket tracker,** none of which is among CronRadar's five channels.
- **Many jobs run more often than every 5 minutes.** Sentry charges the same per monitor whatever the schedule, below CronRadar's $2 Fast price.

## When a Dedicated Cron Monitor Fits Better

- **You don't use Sentry, or you're on its free Developer plan,** where one cron monitor and one user are included and additional monitors require a paid plan.
- **You want job monitoring budgeted apart from error tracking.** Sentry's pay-as-you-go budget is shared with every data category. CronRadar bills monitors only, and its optional budget pauses new monitors, not existing ones.
- **Your jobs are Hangfire recurring jobs, or Quartz.NET 3.x jobs on repeating simple triggers.** Discovery covers every such job without per-job attributes, flags a discovered job that never runs, and pauses one that disappears from the registry. CronRadar.Quartz 0.1.0 can't monitor cron triggers, and a single cron trigger in the scheduler stops it from pausing removed jobs.
- **Success depends on what the run produced,** and you want the job's own counts checked on `complete` instead of through a separate metric pipeline.
- **Alerts should carry owner, severity, and runbook context,** and most of your jobs run hourly or less often, at $0.50 a month each beyond the free 10.

You can also use both: Sentry for exceptions, a dedicated monitor for schedules. To compare other dedicated monitors, see [cron monitoring tools compared](https://cronradar.com/comparisons/best-cron-monitoring-tools). [Datadog cron job monitoring](https://cronradar.com/blog/datadog-cron-job-monitoring), [Dead Man's Snitch vs CronRadar](https://cronradar.com/blog/dead-mans-snitch-comparison), and [Better Stack heartbeats vs a dedicated cron monitor](https://cronradar.com/blog/better-stack-heartbeats) cover three other options. If you move a job, keep the old monitor running until the new one has seen a real run and a test failure.

## Frequently Asked Questions

### What is Sentry Crons?

Sentry Crons, which Sentry's product docs call Cron Monitoring, watches scheduled jobs inside a Sentry project. A job sends check-ins at the start and end of each run, and Sentry creates an issue when a check-in is missed, a run reports an error, or a run exceeds its maximum runtime. Notifications come from alert rules attached to those issues.

### Does Sentry Crons support cron expressions with time zones?

Yes. A monitor takes a five-field crontab or an interval, plus a `timezone` from the tz database such as `America/Los_Angeles`. Sentry recommends setting the time zone for fixed-time schedules like `0 17 * * *`. It doesn't accept seconds or year fields, or schedules shorter than one minute.

### How does Sentry detect a job that runs too long?

With `max_runtime`, in minutes, up to 10,080. A run that sends `in_progress` but no `ok` or `error` within that time is marked failed. Heartbeat-only monitors can't detect overruns, since there's no start to measure from. CronRadar has no equivalent setting; a timeout inside the job turns a hang into a reported failure.

### Can I use Sentry Crons without the Sentry SDK?

Yes. You can send check-ins with plain HTTP requests to the monitor's ingest URL, or wrap a command with `sentry-cli monitors run`, which uses the project's DSN. Both can create the monitor from a schedule sent with the check-in. You still need a Sentry account and project.

### When should I use a dedicated cron monitor instead of Sentry Crons?

When Sentry isn't already central to your team, or you want job monitoring outside your error tracker's budget and alert rules. For .NET teams on Hangfire, CronRadar's discovery is the main practical difference; Quartz.NET discovery in 0.1.0 covers only jobs on repeating simple triggers. If you need time zones, intervals, or a per-run maximum runtime, stay with Sentry Crons.
