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.

.github/workflows/nightly-sync.yml
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.

Try it on your own jobs

14-day free trial with every feature. No card required.

Start your free trial