Cron Jobs in GitHub Actions: Complete Tutorial
GitHub Actions supports scheduled workflows using the schedule trigger and standard cron expressions. This lets you automate tasks like nightly builds, weekly dependency audits, daily data syncs, or monthly reports — all within your repository, with no external scheduler needed.
Basic scheduled workflow
Create a file at .github/workflows/scheduled.yml:
name: Nightly build
on:
schedule:
- cron: '0 2 * * *' # every day at 2:00 AM UTC
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
run: npm testMultiple schedules on one workflow
You can define multiple cron triggers in the same workflow:
on:
schedule:
- cron: '0 8 * * 1-5' # weekdays at 8 AM UTC
- cron: '0 0 * * 0' # Sundays at midnight UTCPractical schedule examples
'*/30 * * * *'— run every 30 minutes (minimum reliable interval is 5 min)'0 6 * * *'— daily report at 6 AM UTC'0 9 * * 1'— weekly summary every Monday at 9 AM'0 0 1 * *'— monthly cleanup on the 1st at midnight'0 12 * * 1-5'— weekday noon deployment
Important limitations and gotchas
- UTC only — GitHub Actions cron always runs in UTC. Convert your local time before writing the expression.
- Minimum interval is 5 minutes — GitHub enforces this; more frequent triggers will be throttled.
- Delays on free plans — scheduled workflows on free/shared runners can be delayed by up to 15–30 minutes during high load.
- Inactive repos — GitHub disables scheduled workflows on repos with no activity for 60 days.
- No seconds field — GitHub Actions uses standard 5-field cron, not 6-field (no seconds).
Checking when a workflow last ran
Go to your repo → Actions tab → click the workflow name → you will see all past runs with their trigger time. You can also manually trigger a scheduled workflow using workflow_dispatch for testing:
on:
schedule:
- cron: '0 2 * * *'
workflow_dispatch: # allows manual trigger from the UISkipping runs when nothing changed
Add a check at the start of your job to exit early if there is nothing to do — this saves runner minutes:
steps:
- uses: actions/checkout@v4
- name: Check for changes
id: check
run: |
git fetch origin main
CHANGES=$(git diff HEAD origin/main --name-only | wc -l)
echo "changes=$CHANGES" >> $GITHUB_OUTPUT
- name: Run build
if: steps.check.outputs.changes > 0
run: npm run buildBuild your cron expression
Not sure how to write the cron string for your schedule? Use the Dev Brains AI Cron Expression Generator — type your schedule in plain English and get the correct expression with a field-by-field breakdown.