GitHub Actions cron not triggering on a branch or a fork
GitHub reads schedules from the default branch only, and a fork does not run them until somebody turns them on. Here is how to check both, and how to test a schedule before merging it.
A cron trigger that looks right and never fires is nearly always being read from somewhere other than where you are looking at it. GitHub reads schedules from one branch only, and in a fork it does not run them at all until somebody turns them on.
Schedules come from the default branch
GitHub's documentation for the schedule event says, as of October 2026, that the event only triggers a workflow run if the workflow file exists on the default branch, that scheduled workflows only run on the default branch, and that they run on the latest commit there. In practice:
- A new scheduled workflow on a feature branch never runs. It starts running after it is merged into the default branch, and not before, however long the branch lives.
- An edit to the cron line on a branch changes nothing. Until it is merged, GitHub keeps using the schedule in the default branch's copy of the file.
- Scheduled runs check out the default branch.
GITHUB_REFis the default branch andGITHUB_SHAits latest commit, so a scheduled run never builds another branch, even if the file lives there too. - Changing the default branch changes the schedule. A new default branch without the workflow file has no schedule. GitHub also makes the person who changed the default branch the actor for the scheduled workflows.
Check which branch GitHub is using, and whether the file is on it:
gh api repos/{owner}/{repo} --jq .default_branch
git ls-tree -r --name-only origin/main .github/workflows/
Testing a schedule before merging it
A schedule cannot be tried out on a branch, but the job behind it can. Add workflow_dispatch next to the schedule:
on:
schedule:
- cron: "17 3 * * *"
workflow_dispatch:
workflow_dispatch also requires the workflow file on the default branch. Once a version of the file is there, you can run any branch's copy of it, with that branch's code:
gh workflow run nightly.yml --ref my-branch
For a brand-new workflow, a temporary push trigger limited to your branch runs it on each push while you work, and comes out again before the merge.
Forks start with schedules off
GitHub's documentation says that when a public repository is forked, scheduled workflows are disabled by default. The upstream repository keeps its schedule; the fork's copy does nothing. To run it in the fork:
- Open the fork's Actions tab and enable workflows for the fork.
- Enable the scheduled workflow itself:
gh workflow list --all --json name,stateshows it asdisabled_fork, andgh workflow enable nightly.ymlturns it on. - Add the secrets it needs in the fork's settings. Secrets belong to a repository and are not copied to a fork.
A fork is a public repository of its own, so its scheduled workflows are disabled after 60 days without activity in the fork. A fork kept only to run the upstream's schedule meets that rule regularly; scheduled workflow disabled after 60 days covers the options.
If the branch is right, check the cron line
- Exactly five fields of POSIX cron: minute, hour, day of month, month, day of week.
- No
@daily,@hourly,@weekly,@monthly,@yearlyor@reboot; GitHub does not support them. - Nothing more often than every 5 minutes.
- UTC unless the entry sets
timezoneto an IANA name, so a schedule written in local time without one runs at a different hour than intended. schedulenested underon:, each entry a list item with acronkey. Quote the expression: YAML reads a value that starts with*as an alias, not a string.
actionlint checks the cron syntax and the timezone name for you. If it all passes and the workflow's state is active on the right branch, the schedule is probably firing late or being dropped at busy times; scheduled workflow runs late or is skipped shows how to tell from the run history.
Find out the first night it does not run
A schedule that was never picked up looks exactly like one that is waiting for its first run, and both look like nothing at all. Having the workflow report to a monitor outside GitHub at the end of each run turns that silence into an alert.
With Vivere, create a heartbeat monitor with the same cron expression and timezone, store its ping URL as a repository secret, and end the job with the Vivere ping action:
- uses: nicholasbergesen/vivere-ping-action@v1
if: always()
with:
url: ${{ secrets.VIVERE_PING_URL }}
status: ${{ job.status }}
If the first scheduled run after a merge never comes, the monitor alerts when its grace period runs out, rather than whenever someone next goes looking. The free plan covers 25 monitors; the GitHub Actions guide has the details.
Common questions
Why is my scheduled workflow not running on my branch?
Scheduled workflows only run on the default branch, using the workflow file as it is in the latest commit there. A schedule on any other branch is ignored until it is merged.
Why don't scheduled workflows run in my fork?
When a public repository is forked, scheduled workflows are disabled by default. Enable Actions in the fork's Actions tab, then enable the workflow, for example with gh workflow enable. The fork also needs its own secrets, and its schedules are disabled after 60 days without activity like any public repository's.
How do I test a scheduled workflow before merging it?
Add workflow_dispatch next to the schedule. Once the workflow file exists on the default branch, gh workflow run with --ref and a branch name runs that branch's copy of the workflow. For a brand-new workflow, a temporary push trigger limited to your branch does the same.
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
- GitHub Actions scheduled workflow stopped running: how to find out why — A GitHub Actions cron workflow stopped firing and nothing told you. The 60-day inactivity rule, the default branch, forks, delays and dropped runs at the top of the hour, edits that drop the schedule, billing, and how to notice the next time.
- Scheduled workflow disabled after 60 days of inactivity: how to keep it running — GitHub disabled your scheduled workflow because the repository had no activity for 60 days. What the rule covers, how to confirm it, how to re-enable the workflow, and the options for keeping it running: real activity, a private repository, an external trigger, keepalive actions and their risks, or an alert when it happens.
- GitHub Actions scheduled workflow runs late or is skipped — Why a GitHub Actions cron workflow starts minutes late or not at all: queueing at high load, the top of the hour, UTC and daylight saving, the 5-minute minimum and concurrency groups. How to measure the delay from run history, and how to trigger a job on time with workflow_dispatch.