SMS, Telegram or Bale? Choosing Alert Channels Without Alert Fatigue
Which downtime alerts should go by SMS and which to Telegram or Bale, how to route by urgency, and how to keep the important alerts from drowning in noise.
When a cron job misbehaves, the first question is always "did it even run?". Cron's own logs answer that; your job's output answers "and what happened?". They live in different places, and many systems keep only one of them by default.
/var/log/syslog or journalctl -u cron; on RHEL-family systems, /var/log/cron or journalctl -u crond.noc724 checks your websites, servers and APIs around the clock and alerts you by SMS, Telegram or Bale the moment something breaks.
| System | Where to look |
|---|---|
| Ubuntu / Debian | /var/log/syslog (lines tagged CRON) or journalctl -u cron |
| RHEL / Rocky / Alma / Fedora | /var/log/cron or journalctl -u crond |
| Alpine (busybox crond) | /var/log/messages when syslog is running |
| macOS | log show --predicate 'process == "cron"' --last 1h |
Useful searches:
grep CRON /var/log/syslog | tail -n 20
journalctl -u cron --since "1 hour ago"
journalctl -u crond --since today | grep backupA typical line looks like this:
Oct 18 03:00:01 web1 CRON[48211]: (deploy) CMD (/opt/app/backup.sh >> /var/log/backup.log 2>&1)If the line is there, cron did its part - the problem is inside the job. If it is missing, look at the schedule, the crontab owner and whether the daemon runs at all: systemctl status cron (or crond).
Redirect both output streams for every job you care about:
0 3 * * * /opt/app/backup.sh >> /var/log/app/backup.log 2>&1>> appends instead of overwriting each run.2>&1 sends errors to the same file - most failures are on stderr.A log full of output with no dates is hard to read. A small wrapper fixes that:
#!/bin/bash
# /usr/local/bin/cronlog - run a command, prefix its output with timestamps
name="$1"; shift
{
echo "=== $(date '+%F %T') start $name"
"$@" 2>&1 | while IFS= read -r line; do echo "$(date '+%F %T') $line"; done
code=${PIPESTATUS[0]}
echo "=== $(date '+%F %T') end $name exit=$code"
} >> "/var/log/cron-jobs/$name.log"0 3 * * * /usr/local/bin/cronlog backup /opt/app/backup.shAdd a logrotate rule for your job logs:
/var/log/cron-jobs/*.log {
weekly
rotate 8
compress
missingok
notifempty
}Work through these in order:
timedatectl.crontab -l for the user, /etc/cron.d/ and /etc/crontab for system jobs.env -i, a minimal PATH and /bin/sh.pgrep -af backup.sh - a stuck previous run plus a lock file can block every new one.Our guide to editing crontab safely covers the environment problems behind most of these failures.
Logs are great for the post-mortem and useless at 3 a.m. if nobody reads them. Two additions turn them into an early warning:
Minimal installs and containers often run no syslog daemon, so cron has nowhere to write. Use journalctl if systemd is present, or redirect job output to files yourself.
Some implementations accept a log-level flag (for example cron -L 15 on Debian's cron). Usually it's simpler to log inside the job itself.
You can't recover output that wasn't captured. Set MAILTO to a working address or add redirection, then wait for the next run.
Written by
The noc724 team
Uptime monitoring engineers
We build and run noc724, an uptime monitoring service that checks websites, servers and APIs from several cities in Iran. These guides come from the problems we and our users run into while keeping services online.
Contact the teamExpert on:Cron Monitoring
Which downtime alerts should go by SMS and which to Telegram or Bale, how to route by urgency, and how to keep the important alerts from drowning in noise.
Why uptime monitors raise false alarms - network blips, timeouts, rate limits, content changes - and the settings that remove them while keeping real alerts fast.
How to set up a simple on-call rotation and escalation policy so every critical alert is answered, without burning out a small team.