I write monitoring so I don't have to watch things by hand. So there is a special kind of dread in discovering that the monitor you installed to watch production has itself been dead since the day you installed it — and that everything you would normally check said it was fine.
This one was dead for five days. Here's the autopsy, because the failure mode is boring, extremely common, and almost invisible.
The setup
I run WhatsApp automation for businesses, and a session can silently drop: the container stays "up", the health check stays green, but the line stops receiving messages. So I built a small watchdog, waha-session-watch, that polls the sessions API every ten minutes and alerts if a line goes quiet. It runs as a dedicated unprivileged user, waha, and needs sudo for one docker call.
The crontab line looked completely ordinary:
*/10 * * * * sudo /usr/local/bin/waha-session-watch >> /opt/waha-watch/cron.log 2>&1
Install it, watch cron.log fill up, move on. Except cron.log never filled up. And I didn't notice, because I wasn't looking at cron.log. I was looking at the tool's own log, watch.log, which had plenty of recent entries. Green across the board.
Five days later a production line went dark for the better part of a day and nothing alerted. That's when I actually went digging.
The first lie: crontab -l
$ crontab -l -u waha
*/10 * * * * sudo /usr/local/bin/waha-session-watch >> /opt/waha-watch/cron.log 2>&1
There it is. It's installed. crontab -l printing your line feels like proof the job runs. It is not. It only proves the line is stored. Whether cron can actually execute it is a completely separate question, and the answer here was no. Not once.
The second lie: a log full of runs
watch.log had entries. Recent ones. So the tool clearly was running... right?
2026-09-08 21:40:12 OK 7 sessions healthy
2026-09-08 21:52:03 OK 7 sessions healthy
2026-09-08 21:59:47 OK 7 sessions healthy
2026-09-08 22:22:15 OK 7 sessions healthy
2026-09-08 22:32:51 OK 7 sessions healthy
Look at the minutes: :40, :52, :59, :22, :32. A */10 cron job fires at :00, :10, :20, :30, :40, on the round ten. These timestamps are ragged. Every single one of them was me, running the tool by hand from the terminal on install day to confirm it worked. Not one line in that log came from cron.
That's the trap in one sentence: a log full of successful runs is not proof of scheduled runs. Manual runs and cron runs land in the same file and look identical unless you read the clock.
The tell: one line in syslog
Here's where it actually gives itself up. I grepped the system log for the job:
$ grep CRON /var/log/syslog | grep waha
... (CRON) info (No MTA installed, discarding output)
... CMD (sudo /usr/local/bin/waha-session-watch >> /opt/waha-watch/cron.log 2>&1)
No MTA installed, discarding output.
That message is the whole case. cron ran the job, the job produced output on stderr, and cron tried to email that output to the user, because that's what cron does with anything a job writes when it can't otherwise capture it. There's no mail server, so it threw the text away. Which means: something printed an error, every ten minutes, into a void, for five days.
If your command's output were really being captured by >> cron.log, cron would have nothing to mail and this line would never appear. (No MTA installed, discarding output) sitting next to a CMD is always a sign the command printed something nobody saw. It is one of the highest-signal lines in all of syslog and almost nobody greps for it.
The root cause: who opens the >>
The bug is the redirect. Specifically, who opens it.
sudo /usr/local/bin/waha-session-watch >> /opt/waha-watch/cron.log 2>&1
It's intuitive to read this as "run the whole thing as root and append to the log." That's not what happens. Redirections are set up by the shell that launches the command, before the command is exec'd, and cron runs this line as the waha user. So the >> /opt/waha-watch/cron.log is opened with waha's permissions, before sudo ever runs.
And the directory:
$ ls -ld /opt/waha-watch
drwxr-xr-x 2 root root 4096 Sep 8 /opt/waha-watch
root:root, mode 755. The cron.log file didn't exist yet, and waha has no write permission in a 755 directory it doesn't own. So the shell can't create the file, the redirection fails, the shell bails out before executing sudo, and the command never runs at all. cron dutifully mails the "permission denied" to nobody.
sudo was never the problem. The unprivileged redirect target in front of it was. The privilege you granted the command is irrelevant when the shell dies setting up its plumbing first.
The fix
Anticlimactic, as these always are:
sudo touch /opt/waha-watch/cron.log
sudo chown waha:waha /opt/waha-watch/cron.log
Create the log file up front, owned by the user cron runs as. First round-numbered timestamp showed up ten minutes later and the watchdog has been real ever since. No code changed. The script was always fine.
What I actually changed
The fix took ten seconds. The lesson took five days, so it's worth more than the fix.
After installing any cron job or systemd timer, verify the first scheduled run in two independent places:
- The scheduler's own record —
grep CRON /var/log/syslogand confirm aCMDline with nodiscarding outputnext to it, at a round-numbered minute. - The tool's own log — an entry whose timestamp lands on the schedule, not a ragged manual one.
If those two don't agree, you have a job that is stored but not running. crontab -l is not on this list on purpose: it can only ever confirm storage.
And the redirect rule specifically: if a scheduled command redirects into a log, create that file ahead of time, owned by the user the scheduler runs as. Don't assume sudo on the command buys you anything for the >>. That file is opened by the caller, not the callee.
I build this kind of infrastructure for a living — production WhatsApp automation for Israeli businesses at Achiya Automation — and this watchdog exists precisely so a silent line doesn't go unnoticed. The irony of the watchdog itself going silent for five days is not lost on me. That's exactly why the two-place verification is now non-negotiable in my install checklist.
Your turn
What's the longest one of your "installed" monitors has been quietly dead before you noticed, and what finally gave it away? I'm collecting the tells that actually work in the wild, beyond the syslog one above. Drop yours below. #discuss
Top comments (0)