keepcalculator

06 Numbers and Powers

Why GitHub Actions Scheduled Workflows Run Late or Get Skipped

Card No. 06·62746 · Updated · Standard formula, shown below

GitHub Actions scheduled workflows (using schedule with cron syntax) often run later than the specified time or are skipped entirely. This happens because GitHub does not guarantee exact execution times and may drop runs when the system is under heavy load. Here is the full explanation of how scheduling works, why delays and skips occur, and what you can do about it.

How GitHub Actions Scheduling Works

When you add a schedule trigger to a workflow file, GitHub uses a cron expression to define when the workflow should run. For example:

on:
  schedule:
    - cron: '15 4 * * *'   # 4:15 AM UTC every day

GitHub evaluates the cron expression and creates a workflow run at the next matching time. However, unlike a dedicated cron service on a server you control, GitHub Actions runs on shared infrastructure. The scheduler queues the job, but the actual execution depends on runner availability, queue depth, and other factors. GitHub officially states that scheduled workflows are not guaranteed to run at the exact minute specified; they may be delayed by several minutes or even longer during peak usage.

Why Delays Happen

Delays occur because GitHub processes millions of scheduled jobs across all repositories. The scheduler is not a real-time system; it is a best-effort queue. When you set a cron time, GitHub adds the job to a queue, and the job waits until a runner is free. The delay is typically small—often under 10 minutes—but can be much larger during high-demand periods, such as after a major release or when many repositories have the same popular time slot (e.g., midnight UTC).

Another factor is the time zone. Cron expressions in GitHub Actions are always in UTC. If you expect a run at 9:00 AM local time but your time zone is UTC+5, the run will be at 4:00 AM UTC. This can make it seem like the run is “late” if you are comparing to local time without converting.

Why Workflows Get Skipped Entirely

Skipped runs are more serious. GitHub may skip a scheduled run entirely for several reasons:

  • Repository inactivity: If a repository has no commits or activity for 60 days, GitHub disables scheduled workflows. You must push a commit or manually trigger the workflow to re-enable it.
  • High load: During periods of extreme load, GitHub may drop scheduled jobs to protect the platform. This is rare but documented in GitHub’s status reports.
  • Workflow disabled: If a workflow is manually disabled in the Actions tab, it will not run.
  • Syntax errors: If the workflow file has a syntax error, the schedule will not be registered, and no runs will appear.
  • Account limits: Free and Pro plans have monthly included minutes. If you exhaust your minutes, scheduled workflows will be skipped until the next billing cycle or until you upgrade.

How to Check if a Run Was Skipped or Delayed

To see what happened to a scheduled workflow, go to the Actions tab in your repository. Look for the workflow name and the event label. Scheduled runs are labeled schedule. If a run was skipped, you may see a message like “This scheduled run was skipped because it would have exceeded the GitHub Actions usage limit.”

For delays, you can compare the created timestamp (when the run was queued) with the started timestamp (when the runner picked it up). The difference is the queue time. GitHub’s API also provides this data via the workflow_runs endpoint.

How to Reduce Delays and Avoid Skips

You cannot force GitHub to run exactly on time, but you can reduce the impact:

  • Choose off-peak times: Avoid the top of the hour and common times like 00:00 UTC. Try 15 or 45 minutes past the hour, or odd hours like 3:17 AM.
  • Use a self-hosted runner: Self-hosted runners are under your control, so the queue delay is usually minimal. However, the scheduler still queues the job; you just avoid the shared runner wait.
  • Add a fallback trigger: Use workflow_dispatch to allow manual triggering, or use a repository dispatch webhook to trigger from an external cron service (like cron-job.org) that calls the API at the exact time you want.
  • Keep the repository active: Make sure you commit regularly (even a README edit) to avoid the 60-day inactivity disable.
  • Monitor usage: Check your Actions usage in Settings > Billing to ensure you have minutes available.

Real-World Example: Delay and Skip

Suppose you set a schedule for 0 2 * * * (2:00 AM UTC). You expect a run at 2:00 AM. You check the Actions tab at 2:10 AM and see no run. At 2:18 AM, the run appears. That is a delay of 18 minutes. This is normal. If you check at 3:00 AM and still no run, it might have been skipped. Look for a notification in the Actions tab or the repository’s audit log. If you see no run at all, check the workflow file for errors and verify the workflow is enabled.

Alternatives to GitHub Actions Schedules

If you need precise timing, consider these alternatives:

  • External cron service: Use a service like cron-job.org or a simple serverless function (e.g., AWS Lambda) that sends a repository dispatch event to GitHub at the exact time you need. This gives you full control.
  • GitHub Actions with a delay tolerance: Build your workflow to tolerate a delay—for example, if the workflow generates a report, it can still run at 2:18 AM instead of 2:00 AM without issue.
  • Use a different CI/CD platform: GitLab CI, CircleCI, and others have their own schedulers with different guarantees. Research their behavior if exact timing is critical.

Frequently Asked Questions

Can I make GitHub Actions run exactly on time?

No. GitHub explicitly states that scheduled workflows are not guaranteed to run at the exact time. The platform is designed for best-effort scheduling, and delays of a few minutes are common. For exact timing, use an external trigger.

Why did my scheduled workflow stop running after a few weeks?

The most common cause is repository inactivity. If no commits are pushed for 60 days, GitHub disables scheduled workflows. You can re-enable by pushing any commit or manually running the workflow via workflow_dispatch.

What does “This scheduled run was skipped” mean?

It means GitHub decided not to run the job, usually due to account usage limits or system load. Check your billing page for included minutes and see if you exceeded them. If not, it may be a temporary platform issue; try again later.

Does the cron time zone matter?

Yes, GitHub Actions cron expressions are always in UTC. If you set 0 9 * * *, that is 9:00 AM UTC, not your local time. Convert your desired local time to UTC before writing the cron expression.

Can I see the queue time for a run?

Yes, in the Actions tab, click on a run and look at the “Created” and “Started” times. The difference is the queue time. You can also use the GitHub API to get these timestamps programmatically.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →