Home › Cron Syntax
Cron Syntax Reference Guide
A complete reference for cron expression syntax. Covers Linux crontab, Quartz Scheduler, AWS EventBridge, Kubernetes and Spring Boot, including all special characters, operators and macros.
Cron Expression Field Structure
A Linux cron expression consists of five fields separated by spaces, followed by the command to execute. Each field defines a time unit, and together they specify exactly when the job should run.
| | | | |
| | | | +-- Day of week (0-6, Sun=0)
| | | +------ Month (1-12)
| | +------------ Day of month (1-31)
| +------------------ Hour (0-23)
+---------------------- Minute (0-59)
Example: 0 9 * * 1-5 runs every weekday at 9:00 AM.
| Field | Required | Values | Special characters allowed |
|---|---|---|---|
| Minute | Yes | 0-59 | * , - / |
| Hour | Yes | 0-23 | * , - / |
| Day of month | Yes | 1-31 | * , - / ? L W |
| Month | Yes | 1-12 or JAN-DEC | * , - / |
| Day of week | Yes | 0-6 (Sun=0) or SUN-SAT | * , - / ? L # |
Special Characters
| Char | Name | Meaning | Example | Result |
|---|---|---|---|---|
* |
Asterisk | Any value - matches every possible value for that field | * * * * * |
Every minute |
, |
Comma | Value list - matches any of the listed values | 0 6,18 * * * |
At 6:00 AM and 6:00 PM daily |
- |
Hyphen | Range - matches any value between start and end (inclusive) | 0 9-17 * * * |
Every hour from 9 AM to 5 PM |
/ |
Slash | Step - defines intervals. */n means every n units |
*/15 * * * * |
Every 15 minutes |
? |
Question mark | No specific value. Used in day-of-month or day-of-week when the other is set. Quartz and AWS only. | 0 0 1 * ? |
First of month, any weekday |
L |
Last | Last day of month or last weekday of month. Quartz only. | 0 0 L * ? |
Last day of every month |
W |
Weekday | Nearest weekday (Mon-Fri) to a given day. Quartz only. | 0 0 15W * ? |
Nearest weekday to the 15th |
# |
Hash | Nth weekday of month. 2#1 = first Monday. Quartz only. |
0 0 ? * 2#1 |
First Monday of every month |
Month Names
| Number | Name | Abbreviation |
|---|---|---|
| 1 | January | JAN |
| 2 | February | FEB |
| 3 | March | MAR |
| 4 | April | APR |
| 5 | May | MAY |
| 6 | June | JUN |
| 7 | July | JUL |
| 8 | August | AUG |
| 9 | September | SEP |
| 10 | October | OCT |
| 11 | November | NOV |
| 12 | December | DEC |
Day of Week Names
| Number | Day | Abbreviation |
|---|---|---|
| 0 or 7 | Sunday | SUN |
| 1 | Monday | MON |
| 2 | Tuesday | TUE |
| 3 | Wednesday | WED |
| 4 | Thursday | THU |
| 5 | Friday | FRI |
| 6 | Saturday | SAT |
Predefined Macros
Linux crontab supports shorthand strings that replace the five-field expression.
| Macro | Equivalent | Description |
|---|---|---|
@yearly / @annually | 0 0 1 1 * | Once a year at midnight on January 1st |
@monthly | 0 0 1 * * | Once a month at midnight on the 1st |
@weekly | 0 0 * * 0 | Once a week at midnight on Sunday |
@daily / @midnight | 0 0 * * * | Once a day at midnight |
@hourly | 0 * * * * | Once an hour at the beginning of the hour |
@reboot | n/a | Once at system startup |
Cron Syntax by Platform
Different platforms use slightly different cron formats. Here is a full comparison.
| Feature | Linux / Unix | Quartz (Java) | AWS EventBridge | Kubernetes | Spring Boot |
|---|---|---|---|---|---|
| Number of fields | 5 | 6-7 | 6 | 5 | 6 |
| Seconds field | No | Yes (1st field) | No | No | Yes (1st field) |
| Year field | No | Optional (7th) | Required (6th) | No | No |
? operator |
No | Yes | Yes | No | Yes |
L operator |
No | Yes | No | No | Yes |
W operator |
No | Yes | No | No | Yes |
# operator |
No | Yes | No | No | Yes |
| Timezone | Server TZ | App TZ | UTC only | Server TZ | App TZ |
| Day/weekday conflict | OR logic | Use ? |
Use ? |
OR logic | Use ? |
Things That Will Actually Bite You
These are not edge cases. Every developer hits at least two of these on their first cron job in production.
Cron's PATH is not your PATH
Cron runs with a stripped-down environment. php, node, python - none of them are on cron's PATH by default.
Your script works fine when you run it manually and fails silently every night. Use full paths:
0 2 * * * /usr/bin/php /var/www/html/script.php
Run which php to find the full path on your server.
Silent failures are the default
By default, cron discards all output. If your script crashes, you will never know. Always redirect both stdout and stderr to a log file:
0 2 * * * /path/script.sh >> /var/log/myjob.log 2>&1
The 2>&1 part redirects errors too. Without it you only see half the picture.
Timezone mismatch
Cron uses the server's system timezone - not UTC, not your local time, not the app's timezone.
Your backup runs at "2 AM" but which 2 AM depends entirely on what timedatectl says.
AWS EventBridge is the only platform that always uses UTC, which is actually a feature.
Check your timezone before assuming anything: cat /etc/timezone
Day-of-month AND day-of-week is OR, not AND
In Linux cron, 0 9 1 * 1 does not mean "the 1st of the month if it falls on Monday".
It means "the 1st of the month, OR any Monday". Most people expect AND logic here and get surprised.
If you need AND logic (run only when both conditions are true), you need to add a date check inside the script itself.
Cron Syntax in CI/CD Platforms
Most CI/CD platforms use standard 5-field Unix cron syntax in UTC. The main difference is where you write it - a YAML file instead of a crontab.
GitHub Actions
Standard 5-field cron in UTC. Minimum interval is every 5 minutes.
on:
schedule:
- cron: '0 2 * * *'
GitHub may delay scheduled runs during peak load.
GitLab CI
Configured in the GitLab UI under CI/CD › Schedules, not in the YAML. Uses standard 5-field syntax.
0 4 * * 1-5
Timezone is configurable per schedule in GitLab.
Jenkins
Jenkins uses standard cron syntax in the triggers block. Also supports H (hash) to spread load across jobs.
triggers {
cron('0 2 * * *')
}
H * * * * means "once per hour at an arbitrary minute" - useful to avoid all jobs firing at :00.
CircleCI
Defined in .circleci/config.yml under triggers. Standard 5-field syntax in UTC.
triggers:
- schedule:
cron: "0 0 * * *"
filters:
branches:
only: main
CircleCI requires a filters.branches block - easy to forget.
Build and validate your cron expression
Paste it in the checker if you are not sure. It takes two seconds and saves you a midnight debugging session.