Cron is one of the most dependable tools on a server, and one of the quietest. It starts your job at the right time, and then it forgets about it. If the job crashes, hangs, or is never started at all, cron does not tell anyone. The backup that stopped last month, the invoice run that did not happen, the cleanup that has been failing since a deployment: all of them look exactly like a working system, right up until the day it matters.
This guide explains how to monitor cron jobs so that a failure reaches you within minutes, and not when a customer or an auditor finds it.
Why cron jobs fail without anyone noticing
Cron’s only job is to launch commands on a schedule. What happens after that is not its concern. That leaves a long list of ways for a job to stop doing its work with no sign of trouble:
- The job never starts. The crontab was overwritten, the server was rebuilt without it, or the cron service itself was stopped.
- The job starts and crashes. A changed path, an expired password, a full disk or a missing dependency makes the script exit early.
- The job hangs. A stuck network call or an unreleased lock leaves the process waiting forever, and the next run is skipped because the last one has not finished.
- The job runs but does nothing useful. It exits successfully having processed zero records, because an upstream change emptied its input.
- The whole machine goes quiet. The server is switched off, out of memory or cut off from the network.
Cron can email the output of a job, but only if mail is configured on the server, and it only helps when something prints. A job that never ran prints nothing.
The core problem: you cannot be alerted by something that did not happen. Cron monitoring works by flipping the question around: instead of waiting for an error, wait for a sign of life and raise the alarm when it does not come.
The idea: have the job check in
The reliable way to monitor a cron job is a heartbeat. Each time the job finishes, it makes a small web request, a check-in, to a unique address. The monitor knows when the next check-in is due. If it does not arrive on time, the job is treated as down and you are alerted.
This catches every failure in the list above with one mechanism, because all of them end the same way: the check-in did not arrive.
How to monitor a cron job, step by step
- Create a check for the job and set its schedule to match the cron expression exactly. A job that runs at 02:00 every night expects a check-in every day, not “roughly daily”.
- Set a grace period longer than the job’s slowest normal run, so a long night does not raise a false alarm.
- Copy the job’s unique address and add a request to it at the end of the cron line.
- Choose where alerts go: email, Slack, Discord, Microsoft Teams, Telegram or a webhook.
- Test it by running the job once, and then by breaking it on purpose, to see the alert arrive.
The one-line version
Chain the check-in after the job with &&, so it is only sent when the job succeeded:
If backup.sh fails, the second command never runs, no check-in is sent, and Pingvella alerts you when the grace period ends. The curl flags are worth knowing: -f treats an HTTP error as a failure, -sS stays quiet except for errors, and -m 10 gives up after ten seconds so a slow network cannot hold up the job.
Failing fast, and timing the run
Waiting for the deadline can mean waiting hours. To hear about a crash immediately, and to measure how long each run takes, signal the start and end of the job and report its exit status:
Ending the address with $? sends the job’s exit status. Zero means success; anything else marks the check down straight away. Run times are recorded, so a job that suddenly takes four times as long shows up as a trend long before it starts overrunning.
Common mistakes
- Checking in at the start only. This proves cron launched the job, not that it finished. Check in at the end, and use the start signal only to measure duration.
- Checking in whatever happens. Using
;instead of&&sends the check-in even when the job failed, which defeats the purpose. - One check for many jobs. A shared check can tell you that something went wrong but not what. Give each job its own.
- A grace period that is too tight. If a job usually takes 20 minutes and sometimes 40, a 10-minute grace period guarantees false alarms, and false alarms teach people to ignore alerts.
- Sharing the check-in address. Anyone who has it can send a check-in for that job. Treat it like a password, and keep it out of public repositories.
- Forgetting maintenance. Pause the check during planned downtime and resume it afterwards, so the next real alert still means something.
What to monitor first
If you have a long list of cron jobs, start with the ones where silent failure is most expensive:
- Backups and database dumps (see how to check your backups are running)
- Billing, invoicing and payment retries
- Email and notification queues
- Data imports, exports and syncs with other systems
- Certificate renewals and security updates
- Cleanup jobs that stop a disk or database from filling up
Cron monitoring for Laravel and other frameworks
Frameworks that run their own scheduler from a single cron entry need one extra step, because the cron entry can fail separately from the tasks it launches. The Laravel scheduler monitoring guide covers how to watch both.
Start with three checks free
Pingvella’s cron job monitoring gives every account three heartbeat checks on the free plan, with alerts when a job goes quiet and again when it recovers. Each check page shows ready-made snippets for cron, shell scripts and Laravel, already filled in with the job’s own address and schedule.
Common questions
How do I know if a cron job stopped running?
Cron doesn’t report missed or failed runs, so have the job check in to Pingvella when it finishes. If the check-in doesn’t arrive on schedule, the check goes down and you are alerted. See the guide to monitoring cron jobs.
How do I send a check-in only when a cron job succeeds?
Chain the check-in after the job with the shell’s AND operator, so the second command only runs if the first succeeded. A failed job then sends no check-in, and the silence raises the alert. To report a failure immediately instead, end the check-in URL with /$?.
What is the difference between late and down?
A late check has passed its expected time but is still within its grace period. A down check has passed that window, or reported a failure. Alerts are sent when a check goes down, and again when it recovers.
There are more answers on the FAQ page.