How to monitor scheduled GitHub Actions workflows
To monitor a scheduled GitHub Actions workflow, add a step that pings a monitoring URL at the start and another that always runs at the end with the job's result. Cronomonitor alerts you when the workflow fails or doesn't run at all.
Why monitor it
GitHub sends failure notifications to whoever last edited the schedule, which is easy to miss. It also says nothing when a scheduled run simply doesn't happen: scheduled runs can be delayed or dropped when GitHub is busy, and in public repositories scheduled workflows are switched off after 60 days without activity.
1. Create a check
Sign up for Cronomonitor, create a project and add a check named after the job. Give it the job's schedule, here the cron expression 0 3 * * * (every day at 3am, UTC), and a grace period that covers how long the job normally takes.
The check gives you a unique ping URL. Every snippet below is also on the check's page in the dashboard, already filled in with that URL and schedule.
2. Add the ping to your job
In the repository, go to Settings › Secrets and variables › Actions and add a secret named CRONOMONITOR_PING_URL containing the check's ping URL. Keeping it in a secret stops anyone who can read the workflow from pinging the check.
Add the ping URL as a repository secret named CRONOMONITOR_PING_URL. GitHub can start scheduled runs several minutes late, so give the check a grace period of 15 minutes or more.
name: Nightly sync
on:
schedule:
- cron: "0 3 * * *" # GitHub runs schedules in UTC
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: curl -fsS -m 10 --retry 3 -o /dev/null "${{ secrets.CRONOMONITOR_PING_URL }}/start"
- run: ./path/to/your-job.sh
- if: always()
run: curl -fsS -m 10 --retry 3 -o /dev/null "${{ secrets.CRONOMONITOR_PING_URL }}/${{ job.status == 'success' && '0' || 'fail' }}"How it works
The final step has if: always(), so it runs whether the job succeeded or failed, and pings with 0 on success or /fail otherwise.
GitHub runs schedules in UTC, so create the check with the same cron expression in UTC. Scheduled runs often start several minutes late, so a grace period of 15 minutes or more avoids false alarms.
Schedules only run on the default branch
A schedule trigger runs the workflow file on the repository's default branch. Adding a schedule on another branch does nothing, and so does a workflow file with a YAML error: in both cases Cronomonitor alerts you that the job didn't run.
Five minutes is the shortest schedule
GitHub runs scheduled workflows at most once every 5 minutes, so don't give the check a shorter schedule.
3. Test it
Run the job once by hand, or use Send a test ping on the check. The check turns green as soon as the first ping arrives, and from then on Cronomonitor expects the next one on schedule.
To check where alerts go, use Send test alert on the check. To see a real failure alert, make the job exit with an error once.
If the check says the job didn't run
The repository's Actions tab lists every run; filter by the workflow to see whether scheduled runs started.
If the workflow shows a "disabled" banner, re-enable it from the Actions tab.
Check the secret name matches exactly: a missing secret becomes an empty string, so the ping goes to the wrong URL.