Monitor GitHub Actions Scheduled Workflows & Alert on Missed Runs
This guide shows you how to set up monitoring for GitHub Actions scheduled workflows and get alerted when a scheduled run doesn’t happen as expected. You’ll learn to use GitHub’s own API and a simple cron-based checker to detect missed runs, and then send alerts via email, Slack, or any other service you prefer.
What You’ll Need
- A GitHub repository that has at least one scheduled workflow (using
scheduletrigger with cron syntax). - Access to create a new repository or add files to an existing one (for the monitor workflow).
- A personal access token (PAT) with
reposcope, or a GitHub App token with actions read permissions, to authenticate API requests. - An alert destination: email (via SMTP), Slack webhook, Microsoft Teams webhook, or any HTTP endpoint you can call.
Step-by-Step
- Identify the workflows you want to monitor. Note the repository (owner/name) and the workflow file name (e.g.,
nightly-build.yml). You can monitor multiple workflows in one go. - Create a new repository or use an existing one for the monitor. It’s often cleanest to have a dedicated repo (e.g.,
workflow-monitor) that only contains the monitoring workflow and its configuration. - Generate a personal access token. Go to GitHub Settings → Developer settings → Personal access tokens → Tokens (classic). Generate a new token with
reposcope. Copy it and store it as a secret in the monitor repository (Settings → Secrets and variables → Actions → New repository secret). Name itGH_TOKEN. - Create the monitoring workflow file. In the monitor repo, create
.github/workflows/monitor-scheduled-runs.yml. This workflow will run every 15 minutes (or your desired frequency) and check the last successful run of each target workflow. - Write the monitoring script. Inside the workflow, add a job that uses
actions/github-scriptto call the GitHub API. The script will:- For each target workflow, fetch the list of runs using
GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs. - Find the most recent run that has a
statusofcompletedand aconclusionofsuccess. - Compare that run’s
created_attimestamp to the current time. If the difference exceeds a threshold (e.g., 2 times the scheduled interval), consider it missed.
- For each target workflow, fetch the list of runs using
- Send an alert when a run is missed. In the script, if a missed run is detected, call an alerting service. For example, send a Slack message using
axiosto a webhook URL stored as a secret. Or send an email via a service like SendGrid or AWS SES. - Set the schedule for the monitor. In the same workflow file, add a
scheduletrigger with a cron expression that runs more frequently than your target workflows. For example, if your target runs daily, check every 6 hours. Usecron: '0 */6 * * *'. - Commit and push the workflow. Push the changes to the monitor repo. GitHub will automatically run the workflow on the schedule. You can also manually trigger it by going to the Actions tab and selecting “Run workflow” to test.
- Test the alert. Temporarily disable or break a target workflow, then trigger the monitor manually. Verify you receive the alert. Then restore the target workflow.
Common Mistakes
- Using the wrong token scope. The token must have
reposcope to read workflow runs. Without it, the API returns a 404 or 403. - Not accounting for timezone differences. GitHub API timestamps are in UTC. Always compare using UTC to avoid false positives.
- Setting the check interval too short. If you check every minute but the target workflow takes 10 minutes to complete, you’ll get false alerts. Make the threshold at least 2–3 times the expected duration.
- Hardcoding the workflow ID. The workflow ID is not the filename; it’s a numeric ID. Use the workflow filename in the API call (e.g.,
nightly-build.yml) to avoid confusion. - Ignoring workflow runs that are still in progress. Always filter by
status=completedandconclusion=successto avoid counting a currently running job as missed.
Tips
- Use a GitHub App instead of a PAT for better security. GitHub Apps have fine-grained permissions and can be scoped to specific repositories. They also don’t expire like PATs.
- Store all configuration in environment variables. Use repository secrets for tokens and webhook URLs, and use environment variables for thresholds and workflow names to make the script reusable.
- Add a “last checked” timestamp. Store the last successful check time in a file or GitHub variable (using the
actions/github-scriptto update a repo variable). This helps avoid duplicate alerts. - Include a link to the workflow runs page in the alert. This makes it easy to jump to the logs and investigate why the run didn’t happen.
- Consider using a third-party service like Cronitor or Healthchecks.io. They offer built-in monitoring for cron jobs and can be simpler to set up, but they require sending a heartbeat from your workflow.
Troubleshooting
- No runs are detected at all. Check that the target workflow has actually run at least once. If it hasn’t, the API will return an empty list. Also verify that the repository name and workflow filename are correct.
- API returns 404. This usually means the token doesn’t have access to that repository, or the repository name is wrong. Ensure the token has
reposcope and the repo is accessible. - API returns 403 (rate limit). GitHub API has a rate limit of 5000 requests per hour for authenticated requests. If you’re monitoring many workflows, you might hit this. Increase the check interval or use conditional requests with
If-Modified-Since. - Alerts not firing. Check the workflow logs in the monitor repo. Look for errors in the script. Also verify that the webhook URL or email configuration is correct.
- False positives. If you get alerts when the workflow actually ran, check the timestamp logic. Make sure you’re comparing UTC times and that the threshold is large enough.
Frequently Asked Questions
Can I monitor workflows in a different organization?
Yes, as long as your token has access to that organization’s repositories. If the org uses SAML SSO, you must authorize the token for the org.
What if my scheduled workflow is disabled?
If a workflow is disabled, it won’t have any recent runs. Your monitor will alert you, which is exactly what you want. Just be aware that the API will return an empty list, so handle that case.
Can I get alerts via email without a third-party service?
You can use GitHub Actions’ built-in actions/github-script to send an email via SMTP, but you’ll need to configure an SMTP server. Alternatively, you can use a service like SendGrid, but that’s an external dependency.
How do I avoid duplicate alerts for the same missed run?
Store the timestamp of the last alert in a file or a GitHub variable. Before sending a new alert, check if the missed run’s timestamp is newer than the last alert timestamp. If not, skip.
Can I monitor workflows that are triggered by push or pull_request?
Yes, the same API works for any workflow, not just scheduled ones. You can adapt the script to check for any workflow that hasn’t run in a certain time, regardless of trigger.
Is there a simpler way using a third-party service?
Yes, services like Cronitor, Healthchecks.io, or Dead Man’s Snitch can monitor cron jobs. You’d add a step to your target workflow that pings the service after completion. If the ping doesn’t arrive, you get an alert. This is often easier to set up than a custom API monitor.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.