Originally published on RunLater.
GitHub documents that on: schedule runs can be delayed when Actions is busy, especially at the start of every hour, and that some queued runs may be dropped. A run set for 09:00 can start minutes later. Since late August 2026, people in a GitHub community thread have reported runs 3 to 12 hours late, and schedules that create no run at all, while manual and API-started runs still start normally.
To start a workflow at a set minute, add a workflow_dispatch trigger and have an outside scheduler call GitHub's dispatch API at that time. The run is queued the moment the call lands, the same as clicking Run workflow. Any scheduler that can send an authenticated POST works.
1. Add a workflow_dispatch trigger
# .github/workflows/nightly.yml
on:
workflow_dispatch:
inputs:
reason:
description: Why this run started
required: false
default: manual
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./scripts/nightly.sh
Remove the schedule: block once the outside schedule is in place, so the job does not run twice. The workflow is then no longer a scheduled workflow, so the rule that switches off schedules in public repositories after 60 quiet days does not apply to it.
2. Create a token that can start the workflow
Create a fine-grained personal access token limited to this one repository, with the Actions repository permission set to Read and write. Give it an expiry date and note it so you can renew it.
3. Schedule the dispatch call
curl -X POST \
https://api.github.com/repos/OWNER/REPO/actions/workflows/nightly.yml/dispatches \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer <token>" \
-H "X-GitHub-Api-Version: 2022-11-28" \
-d '{"ref": "main", "inputs": {"reason": "scheduled"}}'
GitHub answers a good request with a 2xx status (204 No Content by default). When a call fails, the status tells you why:
- 401: the token is wrong or expired
- 404: a wrong owner, repo or workflow file name, or a token without access
- 422: a ref that does not exist, or an input the workflow does not declare
If you keep on: schedule
- Pick a minute other than :00, for example
17 * * * *. The top of the hour is the busiest time. It can help but is no cure: the same thread has reports of off-peak minutes that stopped firing too. - Write the job so a late start is harmless, and check for missed runs from outside GitHub. A schedule that never fires shows no failed run and sends no notification.
- Keep the workflow file on the default branch. Schedules only run from there.
Disclosure
I work on RunLater, one of the schedulers that can make this call: down to once a minute, in your timezone, with retries and a log of every response. Starter is $15/month after a 14-day trial. There's a side by side at RunLater vs GitHub Actions scheduled workflows.
Top comments (0)