The short version: A weekly health check for my capture webhook exited 1 on August 31 and on the next three Sundays. The whole time, the status command I run at the start of every session said ânothing needs attentionâ. It was checking that each timer had a next run scheduled. It never asked how the last run ended.
Get the week's seven in one email, every Monday.
What went wrong
The box runs about twenty systemd timers. My status collector watched them the way youâd check a train timetable: is the next departure posted? A timer with no next run gets flagged as dead. A timer whose job runs on time and fails does not.
So capture-health.service sat in systemdâs âfailedâ state for four weeks, visible to anyone who typed systemctl --failed, invisible on my dashboard. I found it on September 27 because I asked an agent a different question, âwhatâs up with the webhook?â, and it looked.
What we learned
- A schedule check is not a health check. âIt will run againâ says nothing about âit workedâ.
- âFailedâ was ambiguous anyway. That script exits 1 when it has findings, the way grep does. So the fix couldnât be âalert on non-zeroâ. Each unit has to declare which exit codes mean findings and which mean broken.
- The journal already had the answer. Since September 21, systemd had been recording how every run ended: 13,978 timer runs across 18 units, 2 real failures. Nobody was reading that column.
What went right
Fixed on October 5. Each watched timerâs last service result is now classified: success; a declared findings code, which is a warning; any other failure, which is critical; and unreadable, which is unknown and also critical. The classification feeds the collector, the dashboardâs attention list, the nightly digest and the session-start status command. The change came with 136 lines of tests.
How to do it (as of October 2026)
- For every timer you watch, read the serviceâs last result, not just the timerâs next run.
systemctl show <service> -p Result -p ExecMainStatus -p ExecMainExitTimestamphas what you need. - Keep a small table of which exit codes mean âfindingsâ per unit. Everything else non-zero is broken.
- Treat âcanât read the resultâ as critical. Silence is the failure youâre trying to catch.
- Put the classification where you already look. Mine goes into the one command I run at session start.
- Once in a while, run
systemctl --failedby hand. It would have caught this in week one.
Status
Fixed. The four Sundays are the reason my status command has a results column.