Cron tools › Reference
Cron cheat sheet
Everything you look up when writing a cron schedule, checked against each scheduler's own documentation or source. Click any expression in a table to load it into the parser below and see its next runs.
Runs
Try one
The five fields
| # | Field | Values | Names | Example |
|---|---|---|---|---|
| 1 | Minute | 0-59 | — | |
| 2 | Hour | 0-23 | — | |
| 3 | Day of month | 1-31 | — | |
| 4 | Month | 1-12 | JAN-DEC | |
| 5 | Day of week | 0-7 (0 and 7 = Sunday) | SUN-SAT |
Names are the first three letters, case-insensitive. After the fifth field comes the command; in /etc/crontab and /etc/cron.d/ a user name comes first.
Quartz and Spring put a seconds field (0-59) in front; Quartz allows a trailing year and AWS EventBridge requires one (1970-2199).
Special characters
| Char | Means | Example | Supported by |
|---|---|---|---|
| * | every value | all dialects | |
| , | list | all dialects | |
| - | range, inclusive | all dialects. Wrap-around ranges such as 22-2 or FRI-MON work in Quartz, node-cron and Celery; elsewhere write two ranges | |
| / | step: every n-th value of a range | all dialects; AWS not in day of week. Steps restart each hour: */35 fires at :00 and :35. 20/15 (from 20) = 20, 35, 50 in GitHub Actions, Kubernetes and GitLab; in a Linux crontab write 20-59/15 | |
| ? | no specific value | Quartz and AWS (required in exactly one day field); Spring and Kubernetes read it as *; node-cron accepts it too | |
| L | last: day of month L, L-3; in day of week 6L = last Friday (Quartz numbering) | Quartz, AWS, Spring, node-cron, Cloudflare Workers, GitLab. Quartz: L alone in day of week = Saturday. AWS does not document L-n | |
| W | weekday nearest a date: 15W; LW = last weekday | Quartz, AWS, Spring, node-cron, Cloudflare Workers. Quartz never crosses the month: 1W on a Saturday is Monday the 3rd. AWS does not document LW | |
| # | n-th weekday of the month: 2#1 = first Monday (Quartz numbering) | Quartz (1-5), AWS (one # per expression), Spring, node-cron, Cloudflare Workers, GitLab | |
| H | hash: a value derived from the job name | Jenkins only: H, H/15, H(0-29), H(0-29)/10. Plain H in day of month stays within 1-28 | |
| ~ | random value, picked when the crontab is loaded | 6~15 | cronie only — no tool can predict the run time |
@ shorthands
| Macro | Linux (crontab(5)) | Kubernetes | Spring | Jenkins |
|---|---|---|---|---|
| @yearly, @annually | 0 0 1 1 * | yes | 0 0 0 1 1 * | H H H H * |
| @monthly | 0 0 1 * * | yes | 0 0 0 1 * * | H H H * * |
| @weekly | 0 0 * * 0 | yes | 0 0 0 * * 0 | H H * * H |
| @daily | 0 0 * * * | yes | 0 0 0 * * * | H H * * * |
| @midnight | cronie and Debian: same as @daily | yes | 0 0 0 * * * | H H(0-2) * * * |
| @hourly | 0 * * * * | yes | 0 0 * * * * | H * * * * |
| @reboot | when the cron daemon starts — possibly before other system daemons | no | no | no |
node-cron accepts the macros except @reboot; GitLab (fugit) adds @noon. GitHub Actions explicitly rejects all of them, Quartz has none,
and AWS has no macros but offers rate(5 minutes) (singular unit for 1: rate(1 hour)). Jenkins' macros use H, so @daily
is a different time for each job.
Dialects compared
| Dialect | Fields | Day of week | Both day fields set | Time zone | Most often |
|---|---|---|---|---|---|
| Linux (Vixie, cronie) | 5 (+ user in /etc/crontab) | 0-7, 0 and 7 = Sun | OR — unless either field starts with *, then AND | system zone; CRON_TZ in cronie; Debian: none per user | 1 minute |
| Kubernetes CronJob | 5 | 0-6 (7 rejected) | OR — unless either is a bare * | controller's zone; .spec.timeZone (stable since 1.27) | 1 minute |
| GitHub Actions | 5 | 0-6 | not documented | UTC; timezone: key since March 2026 | 5 minutes |
| Google Cloud Scheduler | 5 | 0-6 or 7 | OR unless a field is * | timeZone field, default UTC | 1 minute |
| GitLab (fugit) | 5 (+ optional seconds) | numbers or names | OR; a trailing & forces AND | per schedule | the schedule worker (GitLab.com: every 5 min) |
| Quartz | 6-7 (sec … [year]) | 1-7, 1 = Sun | not allowed: one must be ? | set on the trigger | 1 second |
| AWS EventBridge | 6 (… year), in cron( ) | 1-7, 1 = Sun | not allowed: one must be ? | rules: UTC; Scheduler: IANA zone | 1 minute |
| Spring @Scheduled | 6 (sec first) | 0-7 or MON-SUN | AND | zone attribute | 1 second |
| Jenkins | 5, digits only | 0-7 | AND | TZ= on the first line | 1 minute |
| Azure Functions | 6 (sec first; 5 also) | 0-6 | AND | UTC; WEBSITE_TIME_ZONE | 1 second |
| Celery crontab() | 5 keyword arguments | 0-6, Sun = 0 | AND | UTC by default | 1 minute |
| node-cron | 5 or 6 (sec first) | 0-7 | AND | timezone option | 1 second |
| Vercel | 5, no names | 0-6 | not allowed: one must be * | always UTC | Hobby: once a day (±59 min); Pro: 1 minute |
| Cloudflare Workers | 5 | 1-7, 1 = Sun | not documented | UTC | not documented |
The OR rule in practice: runs on the 1st and on every Monday. Starting a field with * flips Linux cron to AND,
so is "Mondays on odd dates" — but Kubernetes only treats a bare * as unrestricted, so there is
"odd dates or Mondays". Same string, different schedule.
Last day of the month in Linux cron
Linux cron has no L. Run on every day that can be the last one, and let the shell keep only the day whose tomorrow is the 1st:
0 23 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && /usr/local/bin/month-end.sh
date -d tomorrowis GNU date (Linux). On BSD or macOS the equivalent isdate -v+1d +\%d.- The
%is written\%: in a crontab an unescaped%becomes a newline and everything after it is sent to the command's standard input. - Quartz, AWS and Spring say it directly: ,
cron(0 23 L * ? *),0 0 23 L * *.
First Monday of the month in Linux cron
The tempting is wrong: with both day fields set, cron runs on the 1st to the 7th and on every Monday.
Schedule the seven possible dates and test the weekday instead (date +%u prints 1 for Monday … 7 for Sunday):
0 9 1-7 * * [ "$(date +\%u)" = 1 ] && /usr/local/bin/report.sh
For the n-th Monday use days 8-14, 15-21 or 22-28. Quartz writes it as , AWS as
cron(0 9 ? * 2#1 *), Spring as 0 0 9 * * 1#1 — note the different numbers for Monday. The text to cron tool writes these workarounds from a sentence.
Daylight saving time
| Scheduler | What happens when the clocks change |
|---|---|
| cronie | Changes of less than three hours get special handling, but only for jobs at a specific time or with a granularity above one hour. Clocks forward: those jobs whose time was skipped run immediately. Clocks back: they do not run twice. More frequent jobs are scheduled normally. |
| Debian / Ubuntu cron | The same special handling, applied to jobs not written as @hourly and without * in the hour or minute field. |
| GitHub Actions | UTC by default, so no DST. With a timezone:, a 02:30 schedule on the day that hour is skipped runs at 03:00. |
| AWS EventBridge | Rules run in UTC. EventBridge Scheduler with a time zone skips a time that does not exist and runs once in a repeated hour. |
| iCalendar RRULE | Local times that do not exist "MUST be ignored". |
| Vercel, Cloudflare Workers, Google Cloud and Azure defaults | UTC, so the schedule never shifts — but its local time moves by an hour twice a year. |
| Kubernetes, Quartz, Spring, Jenkins, node-cron, Celery | Not in our verified notes. Look at the runs around the change dates in the next run calculator. |
The safest schedule is one that does not care: avoid 01:00-03:00 local time for daily jobs, or run the scheduler in UTC. The crontab checker flags jobs inside the skipped hour.
How to read a cron expression
- Count the fields: five is Linux style, six with seconds first is Quartz or Spring, six ending in a year is AWS.
- Read the time fields left to right — minute, hour — then the day fields.
- If both day fields are set, check the dialect's rule in the table above: OR, AND, or not allowed.
- Check the weekday numbering before trusting any digit in the last field.
- Paste it into the parser at the top of this page to see the next runs in your time zone.
FAQ
Is Sunday 0 or 7?
Both, in Linux cron, Spring, node-cron, Jenkins and Google Cloud Scheduler. Kubernetes, Azure and Celery accept only 0. Quartz, AWS and Cloudflare Workers count 1 = Sunday to 7 = Saturday.
Can cron run every 30 seconds?
Not Linux cron — its smallest unit is the minute. Quartz, Spring, Azure Functions and node-cron have a seconds field: */30 * * * * * in Spring. On Linux, two crontab lines
with one running sleep 30 first is the usual workaround.
Why did my job run on the wrong day?
Usually the day-of-month OR rule (0 0 1-7 * 1 runs every day of the first week and every Monday) or weekday numbering copied between Linux and Quartz or AWS, where 1-5 means Sunday to Thursday.
Can I put a comment at the end of a crontab line?
No. Cron does not allow comments on the same line as a command or an environment setting — put # comments on their own line.