Skip to content
AlertPing

Guides

How to monitor a cron job

| Guides | 9 min read

To monitor a cron job, you invert the direction of the check: instead of a monitoring service polling the job, the job pings the monitoring service every time it finishes successfully. This is called heartbeat monitoring, or a dead man's switch. You give the monitor an expected period and a grace period; if the ping does not arrive in that window, you get paged. It is one line of curl at the end of the job, and it is the only technique that catches a cron job which never ran at all.

Why cron jobs fail silently

A web server is easy to monitor because it answers. A cron job answers nothing. It runs at 3am on a box nobody is watching, and when it stops running it produces exactly the same amount of noise as when it succeeds: none. Hence the backup story every company has. The dump job started failing in March, nobody found out until the restore in July, and those four months of history looked identical to four clean ones.

The three approaches people reach for first share one defect:

  • MAILTO in the crontab. Cron emails you the job's output. In practice the mail is swallowed by a misconfigured local MTA, lands in a folder nobody opens, or is so noisy that everyone filters it. Worse, MAILTO only reports output from a job that ran. If the cron daemon is dead, the box is off, or someone's crontab -r wiped the file, there is no output, so there is no mail. Silence looks identical to success.
  • Checking exit codes in a wrapper script. Better, because you react to failure rather than to text. But the wrapper only helps if the wrapper runs. Kill the machine, the container or the cron daemon, and your careful error handling never executes. Silence again.
  • Logging to a file. A log is a record, not an alarm. It works only if a human reads it, on a schedule, forever. Nobody does this.

The line worth remembering: with cron, the absence of a failure signal is not evidence of success. Only a positive success signal proves the job ran. Every approach above hunts for failure. Heartbeat monitoring watches for missing proof of success instead, which covers every way a job can die without saying a word.

How heartbeat monitoring works

The mechanics take about two minutes, in any language, on any host:

  1. Create a heartbeat monitor with an expected period (every 24 hours, hourly, a cron expression) and a grace period (how late is still acceptable).
  2. The monitor hands you a unique ping URL.
  3. Your job requests that URL as its last action, and only if everything before it succeeded.
  4. If no ping arrives within the period plus the grace, the monitor alerts you.

The ping is an outbound HTTPS request, so the job can sit behind a firewall or on a private subnet and still be monitored. And because the monitor lives somewhere else entirely, it keeps watching when your infrastructure does not. On a server you provision and deploy to yourself, the cron daemon is one more service that can quietly stop, and a monitor running on the same box would stop with it. That is the dead man's switch monitoring principle: the alarm fires because the system stopped reporting, not because it managed to report a problem.

The one-line version with a crontab that pings on success

crontab -e ▸ nightly backup, 3am

0 3 * * * /usr/local/bin/backup.sh && curl -fsS -m 10 --retry 3 https://hb.alertping.com/p/YOUR-MONITOR-KEY > /dev/null

That is a complete cron monitoring setup. The curl flags each stop a specific bad behavior:

  • -f treats an HTTP error (a 500 from the monitoring service) as a failed command, instead of succeeding with an error page as the body.
  • -s silences the progress meter, which otherwise generates a pointless cron email every night.
  • -S puts real errors back on stderr, so an undeliverable ping still surfaces. Use -s and -S together, always.
  • -m 10 caps the request at 10 seconds, so a hung connection cannot leave a cron process alive until the next run collides with it.
  • --retry 3 retries transient network errors. A dropped packet should not page you at 3am.
  • > /dev/null (or -o /dev/null) discards the response body. Same reason as -s: no output, no cron mail.

The mistake everyone makes with && versus ;

The && in that line is the entire point of it. In a POSIX shell, a && b runs b only if a exited 0, so the ping happens only when the backup actually worked.

Write ; instead and the shell runs the ping unconditionally: the backup can explode, exit 1, corrupt its output, and the heartbeat still lands on time. That is monitoring which reports green while the job burns, worse than no monitoring, because it buys you confidence you have not earned. Same trap in a pipeline: backup.sh | tee log.txt returns tee's exit status, not the backup's, unless you set pipefail. Check the character. It is the most common cron monitoring bug, and it is silent by construction.

A wrapper that signals start, success and failure

The one-liner catches a job that did not run. It does not catch a job that started and then hung (what a database dump does when it blocks on a lock), and it does not measure duration. Send three signals instead of one: a start ping, a success ping, and a failure ping carrying the output.

/usr/local/bin/run-backup.sh

#!/usr/bin/env bash
set -uo pipefail

PING="https://hb.alertping.com/p/YOUR-MONITOR-KEY"

# 1. Signal the start. This starts the duration clock and lets the
#    monitor alert on a job that begins but never finishes.
curl -fsS -m 10 --retry 3 -o /dev/null "$PING/start"

# 2. Do the real work. Capture output so a failure can carry it.
OUTPUT=$(/usr/local/bin/backup.sh 2>&1)
CODE=$?

# 3. Report the outcome, and only the truthful one.
if [ "$CODE" -eq 0 ]; then
  curl -fsS -m 10 --retry 3 -o /dev/null "$PING"
else
  printf '%s' "$OUTPUT" \
    | curl -fsS -m 10 --retry 3 -o /dev/null --data-binary @- "$PING/fail"
fi

exit "$CODE"

set -e is deliberately absent: with it, the script would exit the moment backup.sh failed and never send the failure ping, which is precisely the ping you care about. The monitor now knows three things it could not know before: that the job started, that it finished, and how long it took.

In Laravel the scheduler already has hooks for this

If your jobs run through the Laravel scheduler rather than raw crontab, you do not need a wrapper. The scheduler exposes ping hooks on every task, in routes/console.php on Laravel 11 and up, or app/Console/Kernel.php on older versions:

routes/console.php

$schedule->command('backup:run')
    ->dailyAt('03:00')
    ->pingOnSuccess('https://hb.alertping.com/p/YOUR-MONITOR-KEY')
    ->pingOnFailure('https://hb.alertping.com/p/YOUR-MONITOR-KEY/fail');

// Long job? Bracket it to capture run duration:
$schedule->command('reports:build')
    ->hourly()
    ->pingBefore('https://hb.alertping.com/p/OTHER-KEY/start')
    ->pingOnSuccess('https://hb.alertping.com/p/OTHER-KEY');

pingOnSuccess() fires only on a zero exit code: the && semantics from earlier, enforced by the framework. Its sibling thenPing() fires regardless of outcome, so treat it the way you treat ;. These hooks need Guzzle. One catch: the scheduler cannot monitor itself, because every hook depends on the single schedule:run cron entry firing. Put a heartbeat on an everyFiveMinutes() task and you are also monitoring the scheduler, and therefore cron, and therefore the box.

Kubernetes CronJobs use the identical principle

A Kubernetes CronJob gives you retries and pod logs, and still no alarm when the controller is wedged or the image pull fails. Same fix, at the end of the container command:

k8s ▸ cronjob.yaml

spec:
  schedule: "0 3 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: backup
              image: registry.example.com/backup:1.4
              command: ["/bin/sh", "-c"]
              args:
                - /usr/local/bin/backup.sh &&
                  curl -fsS -m 10 --retry 3 -o /dev/null
                  https://hb.alertping.com/p/YOUR-MONITOR-KEY

alertping

Give every scheduled job a heartbeat

Create a monitor, paste one curl line, pick a grace period. The first night the backup skips, your phone knows. SMS, Slack, email and webhooks are included on every plan.

Choosing the period and the grace period

This is where teams either get quiet, trustworthy alerts or train themselves to ignore the pager. The period is when you expect the ping. The grace is how much lateness is normal. Set the grace from the worst run you have actually seen, not the average one. A nightly backup that usually takes 12 minutes will, on the night someone bulk-imported a million rows, take 40. A 15-minute grace pages you at 3:15am for a healthy job that finished at 3:40. Do that twice and the team mutes the channel.

JobPeriodGraceWhy
Nightly backup (12 min typical)1 day30 to 60 minAbsorbs the heavy night, still tells you before breakfast.
Hourly sync1 hour10 to 15 minAbout a quarter of the period: room for jitter and a retry, but you never miss two runs.
Every-5-minute queue worker5 min5 to 10 minNever page on one skipped tick. One to two periods catches a real stall in about 10 minutes.
Weekly report (Mondays)7 days2 to 6 hoursNobody reads it before standup, so hours of grace cost nothing and remove every false page.

Rule of thumb: grace should exceed your worst observed run time, and for short-period jobs it should be at least one full period. If you keep shrinking the grace to catch failures faster, the real problem is a job whose run time is drifting.

Monitor the work, not just the exit code

A heartbeat proves the process ran and exited 0. It does not prove it did anything useful. This is the failure mode that survives naive cron monitoring:

  • The zero-row success. The sync job's API credentials expired. It fetched an empty list, wrote zero rows, exited 0, and pinged cleanly for three weeks while dashboards rendered stale numbers. Green board, dead pipeline.
  • The empty backup. pg_dump exited 0 but the disk filled, so the file is 4KB. The heartbeat cannot tell.
  • The creeping duration. A job that took 4 minutes in January takes 55 now. It has not failed yet. It will, the first time two copies overlap on the same table.

Make the job assert on its own output before it pings. Have the script fail itself when the result is nonsense, and let && do the rest:

assert ▸ a backup smaller than 10MB is not a backup

pg_dump mydb | gzip > /backups/db.sql.gz
SIZE=$(stat -c %s /backups/db.sql.gz)

if [ "$SIZE" -lt 10000000 ]; then
  echo "dump is only $SIZE bytes, refusing to report success" >&2
  exit 1
fi

Now the heartbeat means something specific: not "a process exited 0" but "a backup of plausible size exists". Duration is the other half. The start and success pings bracket the run, so a job whose median run time doubles over a month warns you weeks before the outage. Pair the heartbeats with a server monitoring tool watching disk and memory on the same box, and the cause is usually right there in the graph.

How do I know if a cron job ran?

Have the job tell you. Add a curl to a unique heartbeat URL at the end of the crontab line, after &&, so it fires only on exit code 0. If the ping arrives, the job ran and succeeded. If it does not arrive in the expected window, you are alerted. Grepping syslog only proves cron tried to start it.

How do I monitor a cron job?

Use heartbeat monitoring. Create a monitor with an expected period and a grace period, get its unique ping URL, and append && curl -fsS -m 10 --retry 3 https://hb.alertping.com/p/YOUR-KEY to the job so it pings only on success. When a ping is missed, the service alerts you by SMS, Slack, email or webhook. No agent, and it works behind a firewall.

What is a dead man's switch in monitoring?

A dead man's switch is a monitor that alerts when a signal stops arriving, rather than when an error is reported. The name comes from train controls, where releasing the lever stops the train. Applied to cron: your job checks in on every successful run, and silence triggers the alarm. It catches failures that can never announce themselves.

Why do cron jobs fail silently?

Because cron has no concept of an expected outcome. It fires a command and forgets it. Failures land in mail nobody reads or logs nobody opens, and a job that never runs (dead daemon, wiped crontab, powered-off box, PATH change) produces no output at all. Silence and success look identical from outside, so nothing surfaces until someone needs the result.

Where this fits

Uptime checks tell you the front door is open. Heartbeats tell you the work behind it is still happening. Most teams build the first, skip the second, and find the gap on restore day. AlertPing's cron job monitoring is the heartbeat side: a unique ping URL per job, a schedule and grace period you set, and the same downtime alerts and escalation chains as every other check, on every plan from $49 a month. It sits alongside the checks in how to monitor website uptime, because a backup that stopped running six weeks ago is an outage too. You just have not had it yet.

keep reading

More from the blog

· Comparisons

Dynatrace synthetic monitoring pricing and what HTTP and browser monitors cost per month

8 min read ▸

· Comparisons

CloudWatch Synthetics pricing and what a canary costs per URL, per interval and per Region

8 min read ▸

· Comparisons

OpManager vs WhatsUp Gold pricing for 50 to 1,000 devices, per edition and per add-on

8 min read ▸

· Comparisons

Site24x7 vs PRTG pricing for 50 to 1,000 network devices, with and without add-ons

8 min read ▸

· Comparisons

OpManager pricing 2026 and what ManageEngine OpManager costs per device, per edition and per add-on

9 min read ▸

· Comparisons

Splunk On-Call pricing and cost per user from the published $5 rate to a yearly bill

7 min read ▸

· Comparisons

Rootly pricing and Rootly cost per user for Incident Response and On-Call

7 min read ▸

· Comparisons

FireHydrant pricing and what FireHydrant costs per responder after Freshworks

8 min read ▸

· Comparisons

Zabbix pricing and Zabbix Cloud pricing by tier with the real Zabbix cost per year

8 min read ▸

· Comparisons

Checkmk pricing 2026 and Checkmk cost per host for Pro, Ultimate and Cloud

8 min read ▸

· Comparisons

Nagios XI pricing and Nagios license cost per node, renewal included

8 min read ▸

· Comparisons

incident.io pricing and incident.io cost per user for response and on-call seats

8 min read ▸

· Comparisons

Datadog On-Call pricing vs PagerDuty cost per seat for 4 to 30 person rotations

8 min read ▸

· Comparisons

Datadog On-Call pricing and what a paging seat costs next to PagerDuty and Splunk On-Call

7 min read ▸

· Comparisons

LogicMonitor vs PRTG pricing for a 100 device network, per device against per sensor

7 min read ▸

· Comparisons

SolarWinds alternatives and competitors priced on one estate, from PRTG and LogicMonitor to Datadog

8 min read ▸

· Comparisons

LogicMonitor competitors and alternatives with Datadog, Dynatrace, New Relic, SolarWinds and PRTG priced on one estate

8 min read ▸

· Comparisons

SolarWinds pricing 2026 and how much SolarWinds costs per node, per module and per year

8 min read ▸

· Comparisons

LogicMonitor pricing 2026 and how much LogicMonitor costs per Hybrid Unit, per package and per year

8 min read ▸

· Comparisons

WhatsUp Gold pricing 2026 and how much WhatsUp Gold costs per device, per edition and per year

8 min read ▸

· Comparisons

Cronitor pricing 2026 and how much Cronitor costs per monitor, per user and per year

8 min read ▸

· Comparisons

Hyperping pricing 2026 and how much Hyperping costs per plan, per seat and per monitor

8 min read ▸

· Comparisons

Uptime.com pricing 2026 and how much Uptime.com costs per tier, per check and per year

8 min read ▸

· Comparisons

StatusCake pricing 2026 and how much StatusCake costs per plan, per monitor and per year

8 min read ▸

· Comparisons

PRTG pricing 2026 and what PRTG Network Monitor costs per sensor, per device and hosted

9 min read ▸

· Comparisons

New Relic Synthetics pricing 2026 and what synthetic checks cost per check, per edition and per month

9 min read ▸

· Comparisons

Sentry pricing 2026 and what Sentry costs per plan, per event and per monitor

9 min read ▸

· Comparisons

Checkly pricing 2026 and how much Checkly costs per check run, module by module

9 min read ▸

· Comparisons

Grafana Cloud pricing 2026 and how much Grafana Cloud costs, meter by meter

10 min read ▸

· Comparisons

Statuspage pricing and Atlassian Statuspage costs per subscriber for public and private pages

9 min read ▸

· Comparisons

Opsgenie pricing 2026 and how much Opsgenie costs, plus what you pay to replace it

8 min read ▸

· Comparisons

PagerDuty pricing 2026 and how much PagerDuty costs per user, per plan and per year

8 min read ▸

· Comparisons

Pingdom pricing 2026 and how much Pingdom costs per check, per plan and per year

8 min read ▸

· Comparisons

Uptime monitoring software to pair with Datadog, New Relic or Dynatrace

8 min read ▸

· Comparisons

How much does Splunk Observability Cloud cost? Hosts, editions and synthetic monitoring

8 min read ▸

· Comparisons

AppDynamics pricing 2026 and how much AppDynamics costs, from license cost per vCPU to synthetics

8 min read ▸

· Comparisons

Dynatrace pricing 2026 and how much Dynatrace costs for hosts, synthetic monitoring and log ingest

8 min read ▸

· Comparisons

How much does New Relic cost? Users, data ingest and synthetic checks

9 min read ▸

· Comparisons

Datadog synthetic monitoring pricing and what synthetics really cost per test run

9 min read ▸

· Guides

Uptime guarantee vs uptime monitoring and why your host reports 99.9% when your site was down

9 min read ▸

· Guides

Cloudflare uptime monitoring with health checks, origin monitoring and the blind spots behind the proxy

11 min read ▸

· Comparisons

Status page pricing and what a hosted status page actually costs in 2026

8 min read ▸

· Guides

API monitoring best practices on what to check, how often and how to keep alerts worth answering

10 min read ▸

· Guides

SSL certificate 200 days as the new validity limit and the 47-day lifetime coming next

9 min read ▸

· Guides

SSL certificate expired? What happens and how to fix it

8 min read ▸

· Guides

How often should you check website uptime?

7 min read ▸

· SLAs

Error budget formula, burn rate alerts and the policy that makes it work

11 min read ▸

· Playbooks

Runbook template for incident response that gets used

8 min read ▸

· Guides

What causes website downtime, and how to catch each cause

8 min read ▸

· Playbooks

Incident postmortem template that teams actually use

8 min read ▸

· Playbooks

On-call rotation best practices that keep engineers sane

8 min read ▸

· SLAs

MTTR (mean time to recovery) explained and how to cut it

7 min read ▸

· Guides

Heartbeat monitoring explained and how it works

7 min read ▸

· Guides

Status page examples and what the good ones get right

7 min read ▸

· Guides

API uptime SLA service credit tiers, downtime limits and what a good one costs

8 min read ▸

· Guides

How to create a status page in 6 steps

7 min read ▸

· Guides

Uptime SLA report and what to include, with a worked example

9 min read ▸

· Guides

SLA service credits and how to claim what you get back

8 min read ▸

· Guides

Synthetic monitoring vs uptime monitoring with what each one costs and when you need it

8 min read ▸

· Guides

What is a status page?

6 min read ▸

· Guides

Status page vs uptime monitoring and what the difference is

6 min read ▸

· Guides

What does 99.9% uptime mean?

6 min read ▸

· Guides

What is five nines (99.999%) uptime?

8 min read ▸

· Guides

How to calculate uptime percentage

7 min read ▸

· Guides

SLA vs SLO vs SLI and what the difference is

7 min read ▸

· Guides

Downtime alerts and how to get notified by email, SMS or phone when your website goes down

7 min read ▸

· Guides

How to monitor an online store for downtime

9 min read ▸

· Guides

Why is my Shopify store unavailable?

8 min read ▸

· Comparisons

Better Stack pricing and how much Better Stack costs

8 min read ▸

· Comparisons

UptimeRobot pricing and how much UptimeRobot costs

7 min read ▸

· Comparisons

Site24x7 pricing and how much Site24x7 costs

8 min read ▸

· Guides

What is a dead man's switch in monitoring?

9 min read ▸

· Guides

Why is my WordPress site down?

9 min read ▸

· Guides

How to monitor WooCommerce uptime and checkout

8 min read ▸

· Guides

How to monitor an API for errors, not just uptime

8 min read ▸

· Economics

How much does website downtime cost?

8 min read ▸

· Comparisons

Synthetic monitoring vs real user monitoring

8 min read ▸

· Benchmarks

What is a good uptime percentage?

7 min read ▸

· SLAs

99.99 uptime meaning for SLAs and the real cost of each nine

8 min read ▸

· Guides

How to monitor website uptime

8 min read ▸

· Playbooks

Incident communication examples, templates and outage communication best practices

9 min read ▸

Know the second your site goes down

Checks every 30 seconds, confirmed from a second region, alerts on every channel.

See pricing