AI & automation

GitHub Actions Scheduled Workflows: Cron Syntax, Delays and Limits

Write a GitHub Actions cron schedule correctly, set a time zone, and learn why scheduled runs start late, get dropped or stop after 60 days of inactivity.

By EpiFive Editorial TeamReviewed/updated 9 min readHow we check articles
On this page

A GitHub Actions scheduled workflow uses the schedule event with a five-field POSIX cron expression, runs in UTC unless you set a timezone, and executes only from the default branch. The shortest interval is 5 minutes, but runs can start late at busy times, such as the top of the hour, and in public repositories they are disabled automatically after 60 days without repository activity.

Who this is for and what you will have at the end

This guide is for developers, administrators and automation builders who want a job to run on a timetable, such as a nightly report, a weekly cleanup or a periodic API sync, and who need to understand why a run was late or missing.

At the end you will have:

  • A working scheduled workflow with the right cron syntax and time zone.
  • A manual trigger so you can test without waiting.
  • Protection against overlapping runs.
  • A checklist for diagnosing late, skipped or disabled schedules.

Prerequisites

  • A GitHub repository where you can push to the default branch.
  • Actions enabled for the repository.
  • Familiarity with the YAML structure of a workflow file in .github/workflows/.

Step 1: Create the scheduled workflow

Create .github/workflows/nightly.yml on the default branch:

name: Nightly report
 
on:
  schedule:
    - cron: "17 3 * * *"
  workflow_dispatch:
 
jobs:
  report:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run the job
        run: echo "Running at $(date -u)"

The schedule event triggers a workflow at the time you specify. Two details matter immediately:

  • The workflow file must exist on the default branch for the schedule to trigger. A schedule defined only on a feature branch does nothing.
  • GITHUB_SHA for a scheduled run is the last commit on the default branch, and GITHUB_REF is the default branch. The job always sees the latest default-branch code.

The workflow_dispatch entry adds a Run workflow button on the Actions tab. It also only receives events when the workflow file is on the default branch.

Step 2: Write the cron expression

GitHub Actions uses POSIX cron syntax with five fields:

┌───────── minute (0 - 59)
│ ┌─────── hour (0 - 23)
│ │ ┌───── day of month (1 - 31)
│ │ │ ┌─── month (1 - 12 or JAN-DEC)
│ │ │ │ ┌─ day of week (0 - 6 or SUN-SAT)
│ │ │ │ │
* * * * *

The supported operators are * (any value), , (a list), - (a range) and / (a step). Non-standard shortcuts such as @daily or @reboot are not supported.

ExpressionMeaning
*/15 * * * *Every 15 minutes
17 3 * * *Every day at 03:17
30 5 * * 1-505:30 on weekdays
0 9 1 * *09:00 on the first day of each month
15 4,5 * * *04:15 and 05:15 every day

The 5-minute minimum

The shortest interval you can run scheduled workflows is once every 5 minutes. */5 * * * * is the fastest valid setting, and expressions such as * * * * * don't give you per-minute runs.

Several schedules in one workflow

A single workflow can have more than one schedule entry. Use the github.event.schedule context to see which entry triggered the run:

on:
  schedule:
    - cron: "17 3 * * 1-5"
    - cron: "43 7 * * 6"
 
jobs:
  report:
    runs-on: ubuntu-latest
    steps:
      - name: Which schedule fired
        run: echo "${{ github.event.schedule }}"

Step 3: Set the time zone

Scheduled workflows run in UTC by default. To use another zone, add a timezone key with an IANA time zone name:

on:
  schedule:
    - cron: "30 5 * * 1-5"
      timezone: "America/New_York"

This runs at 5:30 AM Eastern on weekdays. For zones that observe daylight saving time, an hour skipped during the spring-forward change moves to the next valid time. The documentation's example is a 2:30 AM schedule that advances to 3:00 AM. If you stay in UTC instead, remember that the local time of your run shifts by an hour when your region changes its clocks.

Step 4: Prevent overlapping runs

If a run takes longer than the interval, the next one starts while the first is still going. A concurrency group controls this:

concurrency:
  group: nightly-report
  cancel-in-progress: false

By default, a concurrency group allows one running and one pending run: a newly queued run replaces any existing pending run. Setting cancel-in-progress: true cancels the running job when a new one starts, which suits jobs where only the latest result matters but not jobs that must finish, such as a data sync. Group names are case-insensitive.

concurrency can be set at the workflow level or on a job.

Step 5: Test it without waiting

  1. Commit the workflow to the default branch.
  2. Open the repository's Actions tab, choose the workflow in the left sidebar and select Run workflow. The run is a workflow_dispatch run, so it tests the steps, though not the cron timing.
  3. For the schedule itself, set a cron a few minutes ahead, wait, and check the run list. Expect that the run might start late.

You can also start a run from the command line with the GitHub CLI:

gh workflow run nightly.yml
gh run list --workflow nightly.yml --limit 5

Why scheduled runs are late or skipped

High load delays

The schedule event can be delayed during periods of high load of GitHub Actions workflow runs. High load times include the start of every hour. If the load is sufficiently high, queued jobs may be dropped. To reduce the chance of delay, schedule your workflow to run at a different time of the hour. That is why the examples here use minutes such as 17 and 43 rather than 0.

Treat a schedule as "about this time", not as an exact timer. If your task needs exact timing, trigger it from a service built for that and use Actions for the work itself, for instance by calling the workflow_dispatch API from an external scheduler.

The workflow is not on the default branch

Scheduled workflows run on the latest commit on the default branch, and only if the workflow file exists on that branch. If you renamed the default branch or merged the workflow into a different branch, it will not trigger.

Automatic disabling after 60 days

In a public repository, scheduled workflows are automatically disabled when no repository activity has occurred in 60 days. The fix is to enable the workflow again:

  • In the web UI, open the Actions tab, select the disabled workflow in the left sidebar and select Enable workflow.
  • With the CLI, run gh workflow enable WORKFLOW, where WORKFLOW is the name, ID or file name.
gh workflow enable nightly.yml

GitHub's documentation says this rule applies to public repositories. To keep a quiet public repository's schedule alive, you need real repository activity within the window, or you re-enable the workflow when it stops.

A different actor than you expect

Some repository events change the actor for scheduled runs. For example, a user who changes the default branch becomes the actor, and if a user with write access reactivates a disabled scheduled workflow by committing a change to the cron value, that user becomes the actor. Notifications about the workflow go to the user who last modified the cron syntax in the workflow file. On Enterprise Managed Users accounts, the actor's account must be active, and deprovisioning the account in the identity provider stops the run.

If failure emails are going to someone who has left, have a current maintainer make a small edit to the cron line so notifications go to them.

Limits worth knowing

LimitValue
Shortest schedule interval5 minutes
Job execution time on GitHub-hosted runners6 hours; longer jobs are terminated and fail
Workflow run time35 days including waiting and approval time
Trigger events1,500 per 10 seconds per repository
workflow_dispatch inputsUp to 25 top-level properties, payload under 65,535 characters
GITHUB_TOKEN API requests1,000 per hour per repository

Total concurrent jobs on GitHub-hosted standard runners depend on the plan: 20 for Free, 40 for Pro, 60 for Team and 500 for Enterprise.

Troubleshooting

SymptomWhat to check
The workflow never ran on its scheduleIs the file on the default branch? Is the cron valid with five fields? Is the workflow disabled in the Actions tab?
It ran, but 10 or more minutes lateLikely high load. Move the schedule off minute 0 and to a quieter minute.
It stopped running after weeks of workingIn a public repository, check for automatic disabling after 60 days with no repository activity, and enable it again.
It runs at the wrong local timeThe default is UTC. Add timezone or convert the time yourself.
Two runs overlapAdd a concurrency group.
@daily is rejected or ignoredNon-standard cron shortcuts aren't supported. Use five fields.
Run fails with a long-running jobJobs on GitHub-hosted runners stop at 6 hours. Split the job.

For a worked example of a scheduled pipeline that publishes content, see /blog/automate-blog-social-media-posting-claude-github-actions. If the scheduled job signs in to Azure, see /blog/workload-identity-federation-github-actions-azure to avoid storing long-lived secrets.

Checklist

  • The workflow file is on the default branch.
  • The cron has five fields and avoids minute 0 unless exact timing is unimportant.
  • A timezone is set if you think in local time.
  • workflow_dispatch is included for manual testing.
  • A concurrency group prevents overlap.
  • In a public repository, someone knows about the 60-day rule and checks the schedule is still enabled.
  • Whoever last edited the cron line is the right person to receive failure notifications.

References

Questions people ask

What is the shortest interval for a GitHub Actions schedule?

The shortest interval is once every 5 minutes. A cron expression such as */5 * * * * is the fastest you can set, and even then the run can start late when GitHub Actions is under heavy load.

Why did my scheduled workflow not run?

Check that the workflow file is on the default branch, because scheduled workflows run only on the latest commit of the default branch. In a public repository, also check whether the workflow was disabled after 60 days without repository activity. Under very high load, queued scheduled jobs can also be dropped.

What time zone does a GitHub Actions cron schedule use?

UTC by default. You can add a timezone key with an IANA time zone name such as America/New_York to the schedule entry, so a cron time is interpreted in that zone.

How do I trigger a scheduled workflow manually?

Add the workflow_dispatch event to the same workflow, then use the Run workflow button on the Actions tab or the gh workflow run command. The workflow file must be on the default branch for the trigger to work.

GitHub ActionsCronYAMLAutomation
  1. Make.com Pricing Explained: Credits, Operations and Plan Limits

    Understand how Make bills in credits, which plan limits besides credits can affect you, how AI modules change the math, and how to avoid running out mid-month.

    AI & automation8 min read
  2. Queuezen: Open Source Social Media Scheduler You Host Yourself

    Queuezen is an MIT-licensed social media scheduler for 10 networks, with a composer, calendar, queue slots, team approvals and analytics, that you run on your own server. Features, install, demo and FAQ.

    AI & automation7 min read
  3. Zapier Agents vs Make AI Agents: Features and Pricing for Small Teams

    Compare how Zapier Agents and Make AI Agents are built and billed: activities versus credits, limits, tools, extra usage and a cost-check method for small teams.

    AI & automation9 min read
  4. AI Automation ROI: Calculate a Business Case and Payback Before You Build

    A step-by-step method to estimate time saved, build and running costs, and payback for an automation, with worked arithmetic and real pricing inputs.

    AI & automation10 min read
  5. n8n AI Agent Node: Connect Tools and Memory in a Working Workflow

    Build a tool-using AI agent in n8n with the AI Agent node: chat model, Simple Memory, a sub-workflow tool, $fromAI parameters, and fixes for common errors.

    AI & automation11 min read
  6. Make.com Operations and Pricing: Estimate Your Monthly Credit Use

    Learn how Make counts operations and credits, how to estimate a scenario's monthly usage from its schedule and bundle counts, and what happens when credits run out.

    AI & automation10 min read