Monitoring scheduled GitHub Actions workflows

Updated November 2, 2026

The schedule trigger turns GitHub Actions into a free cron service, and plenty of teams run nightly syncs, reports and cleanups on it. It has quirks that are easy to miss, though, and a run that never happens doesn’t show up as a failure anywhere.

What to know about scheduled workflows

Pick an odd minute such as 17 instead of 0 to stay away from the busiest moment of the hour.

A workflow that reports

Save the check’s ping URL as a repository secret called HEARTCRON_URL (Settings → Secrets and variables → Actions), then report the start and the result:

name: Nightly sync
on:
  schedule:
    - cron: "17 3 * * *"   # 03:17 UTC
  workflow_dispatch:

jobs:
  sync:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v4

      - name: Report the start
        run: curl -fsS -m 10 --retry 3 -o /dev/null "${{ secrets.HEARTCRON_URL }}/start" || true

      - name: Sync
        run: ./scripts/sync.sh

      - name: Report the result
        if: always()
        run: curl -fsS -m 10 --retry 3 -o /dev/null "${{ secrets.HEARTCRON_URL }}/${{ job.status == 'success' && '0' || 'fail' }}"

Set up the check

Use the same expression, 17 3 * * *, with the time zone set to UTC. Be generous with the grace period, because runs are often late: 30 to 60 minutes is sensible for a nightly job. You get an alert when the run fails, when it doesn’t finish, and when it doesn’t happen at all, including the day GitHub disables the schedule on a quiet repository.

Many scheduled workflows

Give each workflow its own check, named after the workflow file, and keep their URLs in separate secrets. The Heartcron dashboard then shows at a glance which schedules ran today and how long each one took.

More guides