Vivere

Vivere / Answers

How to get an alert when a GitHub Actions scheduled workflow doesn't run

GitHub notifies you about runs that fail. A scheduled workflow that never runs leaves nothing to fail, so the alert has to come from something that notices the run is missing.

GitHub tells you when a workflow run fails. A scheduled workflow that does not run at all produces no run, so there is nothing to fail and nothing to tell you about. Getting an alert for that case means checking for the run from somewhere that notices its absence.

What GitHub notifies you about

GitHub's notification rules, as of October 2026, send a notification when a workflow run you triggered completes, with an option to be told only about failures. For scheduled workflows the notification goes to the user who created the workflow, or to whoever last changed its cron syntax, or to whoever re-enabled it after it was disabled.

That covers a run that happens and fails, sent to one person. It does not cover:

GitHub Actions scheduled workflow stopped running explains each of these and how to tell them apart.

Option 1: a watchdog workflow in the same repository

A second scheduled workflow can check when the first last succeeded, and fail if that was too long ago:

name: Schedule watchdog
on:
  schedule:
    - cron: "41 */6 * * *"
  workflow_dispatch:

permissions:
  actions: read

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - name: Fail if nightly.yml has not succeeded in 26 hours
        env:
          GH_TOKEN: ${{ github.token }}
          GH_REPO: ${{ github.repository }}
        run: |
          last=$(gh run list --all --workflow nightly.yml --event schedule \
            --status success --limit 1 --json createdAt --jq '.[0].createdAt')
          echo "last successful scheduled run: ${last:-never}"
          test -n "$last"
          age=$(( $(date +%s) - $(date -d "$last" +%s) ))
          test "$age" -lt $(( 26 * 3600 ))

It catches a workflow disabled by hand, a schedule that went missing in an edit and a run of failures. Its limits come from living next to the thing it watches. It is a scheduled workflow too, so in a public repository it is disabled by the 60-day rule on the same day as the workflow it checks. It is delayed and dropped at busy times like any other schedule. Its failure notifications follow the same rules as any scheduled workflow's, so they reach one person. And if Actions is turned off or the quota runs out, neither workflow runs.

Option 2: poll the API from somewhere else

The same check, run from a server or a scheduler outside GitHub, avoids the shared fate: the workflow runs API gives the last scheduled run's time and conclusion, and the poller alerts however you like. It works, and it is a small service you now maintain, with a token to keep valid and a scheduler of its own that can stop without telling anyone.

Option 3: a dead man's switch

Turn the check around. Instead of something asking GitHub whether the workflow ran, the workflow reports each run to a service outside GitHub, and the service alerts when a report is overdue. Every way a scheduled workflow can stop, including the ones that take the whole repository's Actions down, ends in the same place: the report does not arrive. A dead man's switch for your scripts explains the idea and the timing rules in general.

The deadline has to allow for GitHub's delays. Take the longest delay you can see in the workflow's own history:

gh run list --all --workflow nightly.yml --event schedule --limit 50 \
  --json createdAt --jq '.[].createdAt'

then add the job's longest normal run and a margin. A grace shorter than that alerts on slow nights; a much longer one tells you about a dead workflow later than it needs to.

Setting it up with Vivere

Vivere is a hosted dead man's switch. Create a heartbeat monitor with the workflow's cron expression and timezone and the grace worked out above, put the monitor's ping URL in a repository secret named VIVERE_PING_URL, and add the Vivere ping action from the GitHub Marketplace at the start and end of the job:

jobs:
  nightly:
    runs-on: ubuntu-latest
    steps:
      - uses: nicholasbergesen/vivere-ping-action@v1
        with:
          url: ${{ secrets.VIVERE_PING_URL }}
          status: start

      - uses: actions/checkout@v7
      - run: ./scripts/nightly.sh

      - uses: nicholasbergesen/vivere-ping-action@v1
        if: always()
        with:
          url: ${{ secrets.VIVERE_PING_URL }}
          status: ${{ job.status }}

A run that fails or is cancelled alerts at once. A run that never happens alerts when the grace runs out, by email or webhook on the free plan, which covers 25 monitors, and also by Slack, Discord, Telegram or ntfy on paid plans. The action is a single curl call; the GitHub Actions guide shows the same thing without it.

Common questions

Does GitHub notify you when a scheduled workflow doesn't run?

No. GitHub's notifications are for workflow runs that complete, optionally only failed ones, and for scheduled workflows they go to one person: the workflow's creator, whoever last changed its cron syntax, or whoever re-enabled it. A workflow that is disabled, or a run that was dropped, produces no run and no notification.

Can one GitHub workflow check that another one ran?

Yes, with gh run list in a second scheduled workflow that fails when the last successful run is too old. It shares the first workflow's weaknesses, though: in a public repository both are disabled by the 60-day rule on the same day, both can be delayed or dropped at busy times, and neither runs if Actions is off or the quota is used up.

How long should the grace period be for a GitHub Actions heartbeat?

At least the longest start delay in the workflow's own run history, plus its longest normal run, plus a margin. GitHub does not publish a maximum delay, so measure it with gh run list --event schedule rather than guessing.

Find out without looking

Vivere watches for the ping that does not arrive. Add one line to the job, pick where alerts should land, and you hear about the run that never happened.

Start free Read the quickstart

25 monitors, a status page, email and webhook alerts. No card.

Last reviewed October 2026.

Related