Sentry Crons for Node.js: Send Check-ins to Cuckoo
Updated
If your Node.js jobs already use Sentry's cron monitoring, you can send those check-ins to Cuckoo without
touching the job code. Cuckoo accepts Sentry check_in envelopes. Point @sentry/node at a Cuckoo DSN and
Sentry.withMonitor(), Sentry.captureCheckIn() and the cron library instrumentation report to Cuckoo.
Late, failed and overrunning jobs then alert through email, Slack, Discord or webhooks.
There is one trade-off to understand first: what happens to error events. More on that below.
How Cuckoo sees Sentry check-ins
- Your organisation has one DSN, shown in the Cuckoo dashboard:
https://PUBLIC_KEY@cuckoo.metastic.ch/PROJECT_ID. - Monitors are matched by monitor slug. With a
monitorConfig, Cuckoo creates the monitor or updates it with the schedule, time zone, check-in margin and max runtime. in_progresscounts as start,okas success,erroras failure. A missing check-in after schedule plus margin counts as late.- Only check-ins are accepted. Errors and transactions sent to this DSN are dropped.
Step 1: Initialise the SDK
// instrument.mjs — load before your app, e.g. node --import ./instrument.mjs worker.mjs
import * as Sentry from "@sentry/node";
Sentry.init({
dsn: "https://PUBLIC_KEY@cuckoo.metastic.ch/PROJECT_ID",
environment: "production",
});
Step 2: Sentry.withMonitor
withMonitor (SDK 7.76.0+) sends in_progress, runs your callback, then sends ok, or error if the callback
throws or its promise rejects. The third argument is the monitor config used for the upsert:
import * as Sentry from "@sentry/node";
const monitorConfig = {
schedule: { type: "crontab", value: "0 3 * * *" },
timezone: "Europe/Zurich",
checkinMargin: 15, // minutes of grace
maxRuntime: 60, // minutes before an in-progress run counts as failed
};
await Sentry.withMonitor("nightly-export", async () => {
await exportOrders();
}, monitorConfig);
Interval schedules use { type: "interval", value: 2, unit: "hour" }.
Step 3: Manual check-ins with captureCheckIn
When start and end are in different places, send the check-ins yourself and keep the returned checkInId:
const checkInId = Sentry.captureCheckIn(
{ monitorSlug: "hourly-sync", status: "in_progress" },
{ schedule: { type: "crontab", value: "0 * * * *" }, timezone: "UTC", maxRuntime: 20 }
);
try {
await syncInventory();
Sentry.captureCheckIn({ checkInId, monitorSlug: "hourly-sync", status: "ok" });
} catch (err) {
Sentry.captureCheckIn({ checkInId, monitorSlug: "hourly-sync", status: "error" });
throw err;
}
In a short-lived script, call await Sentry.flush(2000) before the process exits so the last check-in is sent.
Auto-instrumentation for cron libraries
The SDK can wrap popular schedulers so every scheduled run sends check-ins. The monitor config comes from the schedule:
import * as Sentry from "@sentry/node";
import cron from "node-cron";
import { CronJob } from "cron";
// node-cron (SDK 7.92.0+): a `name` is required and becomes the monitor slug
const cronWithCheckIn = Sentry.cron.instrumentNodeCron(cron);
cronWithCheckIn.schedule("*/15 * * * *", () => refreshCache(), {
name: "refresh-cache",
timezone: "Europe/Zurich",
});
// cron package (SDK 7.92.0+): the second argument is the monitor slug
const CronJobWithCheckIn = Sentry.cron.instrumentCron(CronJob, "nightly-cleanup");
const job = new CronJobWithCheckIn("0 2 * * *", () => cleanup(), null, true, "Europe/Zurich");
node-schedule is covered by Sentry.cron.instrumentNodeSchedule() (SDK 7.93.0+). Check the Sentry docs for the
library versions each helper supports. Wrapping only works when the scheduler runs inside a long-lived process. If
that process dies, check-ins stop, and Cuckoo marks the monitors late after the margin.
The catch: errors and the DSN
Sentry.init() configures one DSN for the whole process. If that DSN is Cuckoo's, your exceptions go to
Cuckoo and are dropped. Your options:
- Separate process, separate DSN. Run scheduled jobs in a dedicated worker process initialised with the Cuckoo DSN, and keep the web app on your real Sentry DSN. Errors from the worker are then not tracked in Sentry. Log them, or use option 2 for that worker.
- Keep Sentry for errors, ping Cuckoo over HTTP. Leave
Sentry.init()on your Sentry DSN and callfetch("https://cuckoo.metastic.ch/ping/YOUR-UUID", { signal: AbortSignal.timeout(10_000) }), plus/startand/fail. The node-cron guide has a complete wrapper. - Cuckoo only. If you don't use Sentry for errors, point the SDK at Cuckoo and you're done.
The SDK has lower-level client and scope APIs for running a second client, but the cron instrumentation and
withMonitor report through the global client. This guide doesn't depend on that setup.
Schedule and margin advice
checkinMarginis the grace time in minutes. If you leave outcheckinMarginormaxRuntime, Cuckoo applies Sentry's defaults: 1 minute (raised to Cuckoo's minimum if needed) and 30 minutes.- Set
timezonefor crontab schedules. Without it the schedule is read as UTC. maxRuntimeneeds anin_progresscheck-in, whichwithMonitorand the instrumentation send.
Troubleshooting
- Monitor never appears: the first check-in had no monitor config. Pass one, or create the monitor with that slug in the dashboard.
- Last check-in missing in scripts: the process exited before the SDK sent it. Add
await Sentry.flush(). - Every run counted twice: several replicas run the same scheduler. Run it in one process.
- Errors missing in Sentry: that process uses the Cuckoo DSN. See the DSN section above.
FAQ
Do I have to rename my monitor slugs?
No. Cuckoo matches by slug and creates monitors from the config, so existing slugs work unchanged. Only the
DSN in Sentry.init() changes.
Are failureIssueThreshold and recoveryThreshold supported?
They are accepted but ignored. Cuckoo uses schedule, time zone, check-in margin and max runtime, and alerts follow its own late, failure and recovery rules.
Does this work in serverless functions?
Yes, as long as the check-ins are flushed before the function returns. For scheduled functions, withMonitor
followed by await Sentry.flush(2000) is the simplest pattern.
Related guides
- Sentry Crons for Python: Send Check-ins to CuckooPoint the Sentry Python SDK's cron monitoring at Cuckoo: the @monitor decorator, capture_checkin, Celery beat auto-instrumentation and the DSN trade-offs.
- Cron Job Start and Fail SignalsSend /start, success and /fail pings from cron jobs to measure run time and catch hung jobs. Includes a reusable bash wrapper script with trap and exit codes.
- Cron Schedule, Time Zone and Grace PeriodPick the right monitoring schedule for a cron job: period or cron expression, IANA time zones, how cron handles daylight saving time, and sizing grace time.
- Dead Man's Switch Monitoring ExplainedWhat dead man's switch monitoring is, how push-based heartbeat checks differ from pull-based uptime monitoring, and which jobs and systems benefit from each.