Push notifications for cron jobs
Your backup cron has been failing for three weeks. You find out when a customer asks why their data isn’t in the export.
That’s the cron job problem. No news isn’t good news — it’s silence. Jobs fail without telling anyone, and you find out from the wrong source at the wrong time.
The fix is a push notification at the end of every scheduled job: one for success, one for failure. It takes two lines.
The bash approach — works with any cron
The && and || operators give you success/failure branches:
#!/bin/bash
# backup.sh
pg_dump mydb > backup.sql.gz && \
curl -sX POST -d "Backup done · $(du -h backup.sql.gz | cut -f1)" https://trigger.fyi/$TRIGGER_FYI_SECRET_KEY || \
curl -sX POST -d "Backup FAILED" https://trigger.fyi/$TRIGGER_FYI_SECRET_KEYThe && runs the success curl if pg_dump exits 0. The || runs the failure curl if anything before it failed. Set TRIGGER_FYI_SECRET_KEY in your crontab environment or /etc/environment.
This pattern works regardless of what’s running. Node script, Python, compiled binary — wrap it:
if ./run-job.sh; then
curl -sX POST -d "Job finished" https://trigger.fyi/$TRIGGER_FYI_SECRET_KEY
else
curl -sX POST -d "Job FAILED — check logs" https://trigger.fyi/$TRIGGER_FYI_SECRET_KEY
fiThe Node.js approach
If you’re already in Node, the SDK is one await:
import fyi from "trigger.fyi";
async function runBackup() {
try {
await performBackup();
await fyi.log("Backup complete"); // feed only — no push, no noise
} catch (err) {
await fyi.critical(`Backup FAILED: ${err.message}`); // high urgency
}
}fyi.log() writes to the feed without pushing to your phone. Use it for routine completions you want on record but don’t need to wake up for. fyi.critical() sends with high urgency — on Android it can bypass Do Not Disturb; on iOS web push it degrades to a normal push (that’s an iOS platform limitation, not something that can be worked around until native apps exist).
The Python approach
No Python SDK yet. requests does the job:
import os
import requests
KEY = os.environ["TRIGGER_FYI_SECRET_KEY"]
try:
run_nightly_cleanup()
requests.post(f"https://trigger.fyi/{KEY}", data="Nightly cleanup done")
except Exception as e:
requests.post(f"https://trigger.fyi/{KEY}", data=f"Cleanup FAILED: {e}")POST a string, get a notification. That’s the full API.
What the alternatives do differently
Healthchecks.io and Dead Man’s Snitch work the opposite way: your cron pings a URL to say “still alive,” and they alert you when the ping stops. Good for catching a cron that stops running entirely. Doesn’t help you know what happened inside a run that did complete.
Cronitor adds wrapping (cronitor exec your-command) that captures stdout, tracks run duration, and alerts on failures. More infrastructure, more visibility — appropriate if you’re running dozens of jobs and want a dashboard.
PagerDuty and OpsGenie are the full incident management stack — schedules, escalation, acknowledgment. Appropriate when a failed cron should wake a team. Overhead if it’s just you.
trigger.fyi sits at the opposite end: one HTTP POST, notification in your pocket. No account required to start. If you want the ping-based “did it even run?” model, pair it with Healthchecks.io — they complement each other.
Routine completions vs. actual failures
The level distinction matters specifically for crons. A job that runs 50 times a day doesn’t need 50 phone interruptions. fyi.log() keeps the record in your feed without the noise. fyi.critical() is for the 2am failure you need to see immediately.
Most cron monitoring tools treat everything the same urgency. The notification that woke you at 3am for a non-urgent log rotation job is exactly why you eventually mute all of them. Separating signal from record-keeping is the design choice that makes the difference.