Monitor scheduled GitHub Actions: catch the silent 60-day disable
GitHub disables
schedule:
workflows in public repositories after 60 days of inactivity. At most, the person who last changed the cron line may get a warning email.
One signed
workflow_run
webhook catches that, plus failed and hung scheduled runs, with zero YAML
changes. Free for individuals, no monitor cap.
Your assistant can do this for you
Monitor my scheduled GitHub Actions workflows with LastPing
On this page
schedule: triggers.
For general GitHub Actions monitoring (all triggers, webhook setup, failure log detail),
see the GitHub Actions monitoring guide.
This page focuses on the unique problems of the cron / scheduled workflow pattern.
The three ways your scheduled workflow disappears.
A workflow with a schedule:
trigger is fundamentally different from a push-triggered workflow.
It runs on a cadence, by itself, and its failures alert almost nobody.
GitHub silently stops it
After 60 days with no commits, pull requests, releases, or other
activity in a public repository, GitHub automatically disables all
schedule:
workflows. GitHub may email a warning beforehand to the person who last changed the cron line;
the disable itself sends no notification. The Actions tab still shows the
workflow; it simply doesn't fire.
Alert goes nowhere useful
When a scheduled run fails, GitHub emails only the person who last committed to the workflow file's cron expression. That's often whoever set it up years ago: someone who left, whose email filter buries it, or who is on vacation when it matters.
Starts, never finishes
A scheduled run that starts and never completes (stuck step, hung network call, infinite retry) is not a failure yet, so no failure email fires. GitHub's job timeout will eventually kill it, but by then you've missed the window where it mattered.
Setup: one webhook, about two minutes
The same webhook approach as the general GitHub Actions guide, but the key detail for scheduled workflows is the expected schedule window you configure in LastPing, which is what catches the 60-day disable.
Create a CI check in LastPing with your cron schedule
In the console,
create a monitor, choose CI pipeline,
and pick GitHub Actions. Set the expected schedule to
match your workflow's
schedule: cron expression (e.g. daily at midnight)
plus a grace window (e.g. 30 minutes). This is the window after
which a missing run signal opens an incident. Copy the webhook
URL and one-time secret.
https://ping.lastping.dev/ci/github/<check-id>
Register the webhook in your repository
On GitHub: Settings → Webhooks → Add webhook.
- Payload URL: the webhook URL from step 1
- Content type:
application/json - Secret: the one-time secret from step 1
- Events: "Let me select individual events" → tick Workflow runs and Workflow jobs
- Workflow jobs is what supplies the failing step name (
{failing_stage}) in alerts;workflow_runalone cannot, and the field renders empty without it.
Optionally, configure the check to filter to the specific workflow name and branch so only your nightly/scheduled workflow counts against the expected cadence.
Optional: attach failure log to the incident
Add an if: failure() step to your scheduled workflow
that POSTs up to 64 KB of context to the detail endpoint.
The failure reason is stored on the open incident, so the alert
shows where it broke, not just that it broke.
LASTPING_CI_SECRET is the same CI secret this
monitor's webhook signs with: reveal it under the monitor's CI
settings in the console (the reveal is password-gated) and save
it as a repository secret.
Sending it also lifts the throttle: without the token, detail
posts are limited to 60 per 15 minutes per monitor; with it,
there is no limit.
The trailing || true is not decoration: this step only
runs on a job that has already failed, and a hiccup reporting the
failure (a rate limit, a deleted monitor) must never be what
turns the job red a second time.
# optional last step — scheduled workflow
- name: Report failure detail to LastPing
if: failure()
env:
LASTPING_CI_SECRET: ${{ secrets.LASTPING_CI_SECRET }}
LASTPING_CHECK_ID: ${{ secrets.LASTPING_CHECK_ID }}
DETAIL: ${{ github.workflow }} / ${{ github.job }} failed on ${{ github.ref_name }} — run ${{ github.run_id }}
run: |
curl -fsS -X POST -H "Content-Type: text/plain" \
-H "X-LastPing-Token: $LASTPING_CI_SECRET" \
--data "$DETAIL" \
"https://ping.lastping.dev/ci/$LASTPING_CHECK_ID/detail" || true
What this specifically catches for scheduled workflows
The webhook covers the failures that GitHub's built-in notifications miss.
- The 60-day silent disable: no
workflow_runevent arrives in the expected window; the schedule window deadline passes → incident opens. This is the case GitHub never notifies you about. - Failed scheduled runs:
workflow_runarrives withconclusion: failure; incident opens with link to the run and failure detail if you added the optional step. - Hung scheduled runs: a run that starts but never completes within the completion window; the overrun opens an incident before GitHub's job timeout fires.
- Alert to the right people: LastPing alerts your configured destinations (email, Slack, Discord, Telegram, webhook, ntfy, Pushover, Microsoft Teams, Google Chat), not just the last cron committer.
- Recovery: the next successful run closes the incident automatically. No manual reset.
For all GitHub Actions workflows (not just scheduled), see the general GitHub Actions monitoring guide. For cron jobs, Kubernetes jobs, and systemd timers, see the monitoring guides index. AI agents that run on a schedule can also self-register their own monitors via MCP.
Questions people ask
Front-loaded answers: the most important fact first.
-
Why did my GitHub Actions scheduled workflow stop running?
GitHub automatically disables
schedule:workflows in public repositories after 60 days without any commits, pull requests, or other repository activity. GitHub may email a warning beforehand to the person who last changed the cron line, but nothing reaches your team, and the disable itself sends no notification. The workflow YAML is still there and the cron expression is still defined, but the trigger simply stops firing. The only way to detect this from outside is a dead-man's-switch monitor that expects a run signal at your cron cadence and opens an incident when the signal doesn't arrive. -
How is this page different from /monitor/github-actions/?
The general GitHub Actions monitoring guide covers all workflow types including push-triggered, pull-request, and manual workflows. This page focuses specifically on the
schedule:trigger: the 60-day silent disable, the notification gap (only the last cron-committer gets emailed), and why the webhook approach with an expected-schedule window is the right solution for scheduled-only monitoring. -
Does GitHub warn you before disabling a scheduled workflow?
Not reliably. GitHub may email a warning beforehand to the person who last changed the cron line, but nothing reaches your team, and there is no UI indicator that a workflow is approaching the limit. The workflow status page shows successful runs up until the last run, then nothing. There is no 'workflow disabled' event and no notification when the disable happens. The only signal is the absence of runs, which requires an external monitor to detect.
-
Why does this need a schedule window, not just a webhook?
A webhook fires when a run happens. If the run never happens (60-day disable, network issue at GitHub's side, cron drift), no webhook fires. The expected schedule window in LastPing is the key mechanism: if the run signal doesn't arrive within [cron period + grace], an incident opens regardless of whether the webhook ever fired.
-
Does the monitoring work without changing the workflow YAML?
Yes. The primary monitoring mechanism is a signed
workflow_runwebhook registered once in repository Settings > Webhooks. LastPing receives a delivery every time the workflow runs (or should run), verifies the HMAC signature, and tracks the expected schedule externally. No YAML change is needed. Optionally, anif: failure()step can attach the failure log to the incident, but the monitoring itself is YAML-free. -
What does LastPing alert on for scheduled GitHub Actions?
Three signals: (1) a
workflow_runevent arrives withconclusion: failure(the run failed); (2) aworkflow_runevent arrives withconclusion: nullafter the timeout (the run hung); (3) noworkflow_runevent arrives within the expected schedule window plus grace (the run never started, which includes the 60-day silent disable case). -
How do I re-enable a 60-day disabled workflow?
Re-enable it explicitly: Actions → your workflow → Enable workflow in the GitHub UI,
gh workflow enable <workflow>from the GitHub CLI, or the REST API's enable endpoint. These are the routes GitHub documents; a new commit is not one of them. With a LastPing monitor, you find out it's disabled before it's been silent for days. -
Is this free?
Yes. LastPing is free for individuals, and CI/CD monitoring is a first-class feature with no monitor cap. See the general GitHub Actions guide for setup details, or compare free cron monitoring tools.
Your nightly backup is one quiet repo away from disappearing.
One webhook now beats finding out 60 days later. First alert wired up in about two minutes, and LastPing monitors itself the same way.