title: "Alert on a daily cron failure" description: "Wire a nightly cron to BoxWatch so a failed run — or no run at all — pages you within the hour." last_updated: "2026-05-24"

Alert on a daily cron failure

You have a database backup that runs at 3 AM. Two failure modes matter:

  1. It ran and failedpg_dump errored, disk filled, S3 timed out.
  2. It didn't run at all — cron was disabled during a deploy, the VM was rebooted, the user got deleted.

Both are silent on the box itself. The job either exits non-zero into the void or simply doesn't fire. This recipe wires that 3 AM cron so either mode lands in Slack within an hour.

What you'll end up with

  • One BoxWatch cron check called nightly-db-backup.
  • A modified crontab that pings BoxWatch on start, success, and failure.
  • An email alert by default; Slack on top if you want it.

Step 1: create the cron check in BoxWatch

  1. Dashboard → Checks → New check.
  2. Name: nightly-db-backup.
  3. Interval: 24 hours.
  4. Grace period: 60 minutes — your job can be up to an hour late before BoxWatch panics.
  5. Linked server (optional): pick the DB host. Linking lets the check inherit that server's maintenance windows.
  6. Max duration (optional): if your backup takes ~10 minutes, set 30 minutes here and you'll also get an alert when the job runs but won't finish.
  7. Alert types: enable Missed and Fail. Optionally enable Stuck if you set a max duration.
  8. Create.

You'll land on the detail page showing three ping URLs:

  • https://api.boxwatch.app/ping/abc123-... — success
  • https://api.boxwatch.app/ping/abc123-.../start — start
  • https://api.boxwatch.app/ping/abc123-.../fail — fail

The slug is a UUIDv4. It's secret, but it's not a password — there's no auth header. Anyone with the URL can ping it. Don't paste it into a public repo. See Cron checks for the full URL semantics.

Step 2: wrap your cron with start/success/fail pings

Your current crontab line probably looks like this:

# /etc/cron.d/nightly-backup
0 3 * * * root /opt/backup.sh

Change it to:

# /etc/cron.d/nightly-backup
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
 
0 3 * * * root curl -fsS https://api.boxwatch.app/ping/abc123-.../start \
  && /opt/backup.sh \
  && curl -fsS https://api.boxwatch.app/ping/abc123-... \
  || curl -fsS https://api.boxwatch.app/ping/abc123-.../fail/$?

What each line does:

  • PATH=... — cron's default PATH is famously restrictive (/usr/bin:/bin on most distros). If your curl is at /usr/bin/curl you're fine, but adding an explicit PATH= line at the top of the crontab saves a future headache.
  • curl ... /start — tells BoxWatch the job has begun. Enables duration tracking and stuck-job alerts.
  • && /opt/backup.sh — runs the backup only if the start ping succeeded. If BoxWatch is unreachable, the job still runs (because that's almost certainly what you want), but only because of the || branch below.
  • && curl ... /success — fires only when backup.sh exits 0.
  • || curl ... /fail/$? — fires when anything before this exited non-zero. $? is the previous command's exit code. The catch: this || runs if backup.sh failed or if the start ping failed. In practice that's fine — you want an alert either way.
💡

If you want a stricter wrapping that distinguishes "start ping failed" from "backup failed," push the logic into a wrapper script (/opt/backup-wrapped.sh) and call that from cron. Easier to read, easier to test, easier to add log capture.

Step 3: verify

Don't wait for 3 AM. Run the wrapped command by hand:

curl -fsS https://api.boxwatch.app/ping/abc123-.../start
/opt/backup.sh
curl -fsS https://api.boxwatch.app/ping/abc123-...

Open the cron check's dashboard page. You should see:

  • Last ping: just now
  • Last success: just now
  • Status: up

If the dashboard still says "no pings yet" after 30 seconds, your curl isn't reaching BoxWatch. Check curl -v output for HTTP 200 OK and {"ok": true}.

Step 4: connect a notification channel

Email alerts are on by default — they go to your account email. To add Slack:

  1. Dashboard → Account → Notifications.
  2. Paste your Slack incoming-webhook URL.
  3. Save.

From the next state transition forward, alerts post to Slack as well as email. See Slack alerts for the message format.

Anti-flapping behavior

BoxWatch only alerts on state transitions. If your job ran successfully yesterday and successfully today, no alert. If today's run failed, you get one alert — then BoxWatch goes quiet until either it succeeds again (recovery alert, if enabled) or it fails again tomorrow (you don't get re-paged on the same incident).

A one-off blip — single failed ping followed by a successful one a minute later — doesn't double-alert.

Common gotchas

  • Cron's restricted PATH breaks curl. Either use the full path (/usr/bin/curl) or set PATH= at the top of the crontab, as shown above.
  • set -e scripts don't always exit non-zero. Backup scripts that pipe through | true or trap errors can return 0 even on partial failure. Test the failure case explicitly: false; echo "exit was $?".
  • Silent-on-success scripts. If your script suppresses output, your && curl ... /success still fires correctly because shell exit codes don't depend on stdout. You're fine.
  • Cron's email-on-output. If you don't want cron emailing you every time the start ping prints a {"ok":true} blob, add > /dev/null after each curl, or use MAILTO="" at the top of the crontab.

For the full set of cron-check alert types (missed, fail, stuck, long), see Cron checks.

Was this page helpful?