Cron Expression Cheat Sheet
A complete reference for cron expression syntax: all 5 fields, special characters, 20 common patterns, and platform differences for GitHub Actions, Kubernetes, Vercel, and crontab.
Table of contents
TL;DR: A cron expression has 5 space-separated fields: minute hour day-of-month month day-of-week. Use * for “any”, / for step values, and , to list multiple values. 0 9 * * 1 runs at 9 AM every Monday.
Cron expression syntax
A standard cron expression is a string of five fields separated by spaces:
┌───────────── minute (0–59)
│ ┌─────────── hour (0–23)
│ │ ┌───────── day of month (1–31)
│ │ │ ┌─────── month (1–12)
│ │ │ │ ┌───── day of week (0–6, Sunday = 0)
│ │ │ │ │
* * * * *
Each field accepts a number, a wildcard, or a combination of special characters.
The 5 fields
| Field | Allowed values | Notes |
|---|---|---|
| Minute | 0–59 | |
| Hour | 0–23 | UTC in most schedulers |
| Day of month | 1–31 | |
| Month | 1–12 or JAN–DEC | |
| Day of week | 0–6 or SUN–SAT | 0 and 7 both mean Sunday in most systems |
Special characters
| Character | Meaning | Example | Description |
|---|---|---|---|
* | Any | * * * * * | Every minute |
, | List | 0 9,17 * * * | At 9 AM and 5 PM |
- | Range | 0 9-17 * * 1-5 | Every hour from 9–17 on weekdays |
/ | Step | */15 * * * * | Every 15 minutes |
? | No specific value | 0 0 ? * MON | Kubernetes/Quartz only; means “don’t care” |
L | Last | 0 0 L * * | Last day of the month (Quartz only) |
# | Nth weekday | 0 0 * * 1#2 | Second Monday of the month (Quartz only) |
20 common patterns
| Expression | Description |
|---|---|
* * * * * | Every minute |
*/5 * * * * | Every 5 minutes |
*/15 * * * * | Every 15 minutes |
0 * * * * | Every hour |
0 */2 * * * | Every 2 hours |
0 9 * * * | Every day at 9 AM |
0 0 * * * | Every day at midnight |
0 9,17 * * * | At 9 AM and 5 PM daily |
0 9 * * 1-5 | Every weekday at 9 AM |
0 9 * * 1 | Every Monday at 9 AM |
0 0 1 * * | First day of every month at midnight |
0 0 L * * | Last day of the month (Quartz) |
0 0 1 1 * | January 1st at midnight |
0 0 * * 0 | Every Sunday at midnight |
30 8 * * 1-5 | Weekdays at 8:30 AM |
0 0 1,15 * * | 1st and 15th of each month |
0 6 * * 1 | Monday at 6 AM |
*/30 9-17 * * 1-5 | Every 30 min during business hours |
0 0 * * 5 | Every Friday at midnight |
0 12 * * * | Every day at noon |
Platform differences
Each platform that uses cron syntax has its own quirks. Here is what to watch for.
GitHub Actions
GitHub Actions uses a 5-field standard cron in UTC. Place the schedule in your workflow YAML:
on:
schedule:
- cron: '0 9 * * 1-5' # Weekdays at 9 AM UTC
Caveats:
- The minimum interval is 5 minutes (shorter schedules are silently clamped).
- Scheduled workflows on the default branch only.
- GitHub may delay runs by up to several minutes under heavy load.
- Workflows on a repository with no activity in 60 days are automatically disabled.
Kubernetes CronJob
Kubernetes uses standard 5-field cron, also in UTC by default:
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-cleanup
spec:
schedule: "0 2 * * *" # 2 AM UTC daily
jobTemplate:
spec:
template:
spec:
containers:
- name: cleanup
image: my-cleanup-image
restartPolicy: OnFailure
Caveats:
- Set
spec.timeZone: "America/New_York"(Kubernetes ≥ 1.27) to use a non-UTC timezone. spec.concurrencyPolicy: Forbidprevents overlap if the previous job is still running.spec.startingDeadlineSecondscontrols how late a missed job can start.
Vercel cron
Vercel Cron Jobs (available on Pro and Enterprise) configure scheduled function invocations via vercel.json:
{
"crons": [
{
"path": "/api/cron/daily-report",
"schedule": "0 8 * * *"
}
]
}
Caveats:
- Maximum 2 cron jobs on Pro, 40 on Enterprise.
- The invoker sends a
GETrequest to your route; authenticate withCRON_SECRET. - All schedules run in UTC.
- Minimum interval is 1 minute.
Standard crontab
The classic Unix crontab (edit with crontab -e):
# minute hour day month weekday command
0 9 * * 1-5 /usr/local/bin/send-report.sh
*/5 * * * * /usr/bin/check-status.sh >> /var/log/check.log 2>&1
Caveats:
- Runs in the timezone of the server unless
CRON_TZorTZis set at the top of the file:TZ=America/New_York. - Environment variables are minimal — set
PATHexplicitly in the crontab or script. - Output goes to the local user’s mail unless redirected.
Common mistakes and how to debug
Mistake: wrong timezone
Most hosted cron schedulers run in UTC. A job set to 0 9 * * * runs at 9 AM UTC, which is 2 AM Pacific or 5 AM Eastern. Always specify and document the timezone.
Mistake: confusing */n with a specific value
*/15 means “every 15 minutes starting from 0” (i.e., 0, 15, 30, 45). It is not the same as 15 (which fires only at minute 15).
Mistake: day-of-month + day-of-week interaction
When both the day-of-month and day-of-week fields are set to a non-wildcard value, most cron implementations (including Vixie cron) treat them as OR, not AND. 0 0 1 * 1 fires on both the 1st of every month and every Monday.
Mistake: running more often than intended
* * * * * runs every minute, 1440 times per day. Add the hour field: 0 * * * * runs once per hour.
Debugging approach:
- Use epochkit’s Cron Builder to paste your expression and see the next 5 execution times.
- Check logs immediately after the expected fire time — many platforms log missed runs.
- If unsure about timezone, add a one-time job that logs the current time and compare.
- On Linux,
grep CRON /var/log/syslogshows cron daemon output.
Related tools
Need to build or validate a cron expression interactively? epochkit’s cron builder shows a human-readable description of any expression and previews the next 5 scheduled runs — no sign-up required.