CrontiqGuides › Monitor Cron Jobs

How to Monitor Cron Jobs in 2026 — Complete Guide

Cron jobs power the invisible backbone of modern infrastructure. Database backups, ETL pipelines, certificate renewals, report generation, cache warming — they all run on schedules, often on machines nobody is watching. The problem is simple: when a cron job fails, nothing happens. No alert. No notification. Just silence.

This guide covers every practical approach to monitoring cron jobs, from basic log tailing to fully automated anomaly detection. By the end, you will have a working monitoring setup that takes under two minutes to configure.

Why Cron Jobs Fail Silently

The cron daemon was designed in the 1970s. Its failure notification mechanism is email — specifically, local email via sendmail. On most modern servers, local mail delivery is either disabled or nobody reads the mailbox. This means a cron job can fail every single run for weeks before anyone notices.

Common failure modes include:

Approach 1: Log Files

The simplest monitoring approach is redirecting output to a log file:

*/5 * * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1

This captures both stdout and stderr. You can then grep the log for errors, or use logrotate to manage file size. The problem: nobody checks log files proactively. You only look when something is already broken.

Approach 2: Wrapper Scripts

A step up is wrapping your cron command in a script that checks the exit code and sends an alert:

#!/bin/bash /opt/scripts/backup.sh if [ $? -ne 0 ]; then curl -X POST https://hooks.slack.com/... \ -d '{"text": "Backup failed on prod-db-01"}' fi

This works but has several drawbacks. You must write and maintain a wrapper for every job. You only detect failures, not missed runs. If the server itself goes down, no alert fires because the wrapper never executes. And you have no visibility into trends — was the job slower than usual? Did the row count drop?

Approach 3: Dead Man's Switch (Heartbeat Monitoring)

The industry standard for cron monitoring is the dead man's switch pattern, also called heartbeat monitoring. The idea: your cron job pings an external service after each successful run. If the service does not receive a ping within the expected interval, it alerts you.

This solves the "server is down" problem because the monitoring service is external. It also detects missed runs, not just failures.

How Crontiq Works — Step by Step

Crontiq implements the dead man's switch pattern with zero configuration. Here is how to set it up:

Step 1: Get Your API Key

Register at crontiq.io/register with your email. You will receive an API key that looks like cq_live_80381902d7b36613.

Step 2: Add a Curl Call to Your Cron Job

Append a single curl command to your existing crontab entry:

# Before (no monitoring) 0 2 * * * /opt/scripts/backup.sh # After (monitored by Crontiq) 0 2 * * * /opt/scripts/backup.sh && curl -s https://ping.crontiq.io/p/cq_live_80381902d7b36613/nightly-backup > /dev/null

That is the entire setup. The first time the ping hits Crontiq, the monitor nightly-backup is automatically created. No dashboard configuration needed. Crontiq infers the expected schedule from the ping frequency.

Step 3: Signal Failures Explicitly

To distinguish between success and failure, use the /fail endpoint:

0 2 * * * /opt/scripts/backup.sh \ && curl -s https://ping.crontiq.io/p/cq_live_80381902d7b36613/nightly-backup > /dev/null \ || curl -s https://ping.crontiq.io/p/cq_live_80381902d7b36613/nightly-backup/fail > /dev/null

The && runs the success ping only if the script exits with code 0. The || runs the fail ping if it exits with any other code.

Step 4: Send JSON Metrics (Optional)

Crontiq's real power is its Magic Engine. If your cron job produces data, POST it as JSON:

ROWS=$(psql -t -c "SELECT count(*) FROM orders WHERE created_at > now() - interval '1 day'") DURATION=$SECONDS curl -s -X POST https://ping.crontiq.io/p/cq_live_80381902d7b36613/nightly-backup \ -H "Content-Type: application/json" \ -d "{\"rows\": $ROWS, \"duration_seconds\": $DURATION}"

Crontiq automatically extracts every numeric value from the JSON, stores it as a time series, and applies anomaly detection. If rows suddenly drops from 10,000 to 50, or duration_seconds jumps from 120 to 3,600, you will get an alert.

Anomaly Detection Explained

Crontiq uses a rolling average over the last 10 data points for each metric, combined with a 2-sigma threshold. In plain terms: if a value deviates more than two standard deviations from recent history, it is flagged as anomalous.

This approach requires zero configuration. You do not define thresholds, expected ranges, or alert conditions. Crontiq learns what "normal" looks like from the data your jobs send. It works for row counts, file sizes, durations, error rates, or any numeric value.

When an anomaly is detected, the monitor status changes to WARNING (shown in purple on the dashboard and badge), and an alert email is sent. Alert emails are rate-limited to one per hour per monitor to prevent spam during sustained anomalies.

Nested JSON and Complex Payloads

Crontiq handles nested JSON structures by flattening them with dot notation:

curl -s -X POST https://ping.crontiq.io/p/cq_live_.../etl-pipeline \ -H "Content-Type: application/json" \ -d '{ "source": {"rows_read": 45000, "errors": 0}, "dest": {"rows_written": 44998, "duration_ms": 12400} }' # Crontiq extracts: # source.rows_read = 45000 # source.errors = 0 # dest.rows_written = 44998 # dest.duration_ms = 12400

Each metric gets its own time series and anomaly detection. No schema definition required.

Comparison to Alternatives

Several services offer cron monitoring. Here is how Crontiq differs:

Real-World Example: Database Backup Monitoring

Here is a complete example for monitoring a PostgreSQL backup job:

#!/bin/bash # /opt/scripts/pg_backup.sh set -e START=$SECONDS DUMP_FILE="/backups/pg_$(date +%Y%m%d).sql.gz" pg_dump mydb | gzip > "$DUMP_FILE" SIZE=$(stat -c%s "$DUMP_FILE") DURATION=$((SECONDS - START)) TABLES=$(psql -t -c "SELECT count(*) FROM information_schema.tables WHERE table_schema='public'" mydb) curl -s -X POST https://ping.crontiq.io/p/cq_live_.../pg-backup \ -H "Content-Type: application/json" \ -d "{\"size_bytes\": $SIZE, \"duration_s\": $DURATION, \"tables\": $TABLES}" \ > /dev/null

With this setup, Crontiq monitors three things simultaneously: backup file size (detects truncated or corrupt dumps), duration (detects performance degradation), and table count (detects accidental schema changes). All without any configuration on the Crontiq side.

Start monitoring your cron jobs — free