GitHub Actions can’t run on a schedule out of the box; you have to explicitly tell it when to run using cron expressions.
Let’s see a workflow that runs every day at 9 AM UTC:
name: Scheduled Workflow
on:
schedule:
# Runs at 9:00 AM UTC every day
- cron: '0 9 * * *'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run a one-time script
run: echo "Hello, world!"
This schedule event is the key. You define a list of cron expressions, and GitHub Actions will trigger the workflow for each one.
The cron expression itself is a string of five fields: minute hour day-of-month month day-of-week.
- Minute:
0-59 - Hour:
0-23 - Day of Month:
1-31 - Month:
1-12 - Day of Week:
0-6(Sunday=0 or 7, Monday=1, …, Saturday=6)
You can use * as a wildcard for any value. For example, * * * * * would run every minute.
Here are some common examples:
0 0 * * *: Run at midnight UTC every day.30 10 * * 1-5: Run at 10:30 AM UTC every weekday (Monday to Friday).0 0 1 * *: Run at midnight UTC on the first day of every month.*/15 * * * *: Run every 15 minutes.
GitHub Actions executes these cron jobs based on the UTC timezone. This is a critical detail; if your users or resources are in a different timezone, you need to calculate the UTC equivalent. For instance, 9 AM PST (Pacific Standard Time, UTC-8) would be 17 9 * * * (9 AM + 8 hours = 5 PM UTC).
You can also use specific ranges and lists. For example, to run at 9 AM and 5 PM UTC every day:
on:
schedule:
- cron: '0 9,17 * * *'
This workflow will trigger twice a day, once at 9 AM UTC and again at 5 PM UTC.
The actual execution of the cron job is not instantaneous. GitHub Actions has a mechanism to enqueue and process these scheduled events. It’s designed to be reliable but not millisecond-precise. If your task requires exact timing, you might need to look at external scheduling services that can trigger a specific workflow run via the API.
A common point of confusion is the day-of-month and day-of-week fields. If you specify both, the job will run if either condition is met. For example, 0 0 1 * 0 would run at midnight UTC on the 1st of every month and every Sunday. This can lead to more frequent runs than intended if you’re not careful. To run only on the first Sunday of the month, you’d need a more complex approach, perhaps using a run step to check the date within the job itself or a separate workflow that’s triggered by the other.
The schedule event in GitHub Actions is a powerful tool for automating repetitive tasks directly within your repository’s CI/CD pipeline. It simplifies setting up recurring jobs without external cron daemons or cloud schedulers, keeping your automation logic close to your code.
The next thing you’ll likely want to do is use the github.event.schedule context variable to differentiate between workflow runs triggered by a schedule and those triggered manually or by other events.