Cron Expressions, Explained

By Udit Agarwal · Updated October 18, 2026

A cron expression is a schedule written as five fields separated by spaces: minute, hour, day of the month, month and day of the week. The expression 0 9 * * 1-5, for example, means 9:00 every Monday to Friday. The format comes from cron, the Unix job scheduler, and the same crontab syntax now drives CI pipelines, Kubernetes jobs and cloud schedulers, with small differences that are worth knowing.

The five fields

Each line in a crontab has five time fields followed by the command to run:

30 2 * * * /usr/local/bin/backup.sh

That line runs the backup script at 2:30 every night. The fields always come in this order:

Position Field Allowed values
1 Minute 0–59
2 Hour 0–23
3 Day of month 1–31
4 Month 1–12
5 Day of week 0–7 (0 and 7 are both Sunday)

Hours use the 24-hour clock. Most implementations also accept three-letter names such as JAN or MON in the last two fields. System-wide files such as /etc/crontab also name a user before the command.

Special characters: * , - /

Four characters cover almost every schedule:

Character Meaning Example Matches
* every value * in the hour field every hour
, a list 1,15 in day of month the 1st and the 15th
- an inclusive range 1-5 in day of week Monday to Friday
/ a step */15 in the minute field 0, 15, 30 and 45

A step counts from the start of its range, so 0-30/10 in the minute field means 0, 10, 20 and 30. Steps that do not divide evenly restart at the boundary: */7 in the minute field runs at 0, 7, 14 and so on up to 56, then at 0 again only four minutes later. Likewise */2 in the day-of-month field gives the odd days, so after a 31-day month the job runs on the 31st and the 1st back to back. Put the step after * or a range; a bare number before the slash, as in 5/15, means different things to different schedulers.

Common cron expressions

Expression When it runs
*/5 * * * * every 5 minutes
0 * * * * at the start of every hour
0 */6 * * * every 6 hours: 0:00, 6:00, 12:00 and 18:00
0 9 * * 1-5 9:00 on weekdays
*/15 9-17 * * 1-5 every 15 minutes from 9:00 to 17:45 on weekdays
0 0 * * 0 midnight every Sunday
0 0 1 * * midnight on the first day of every month

In the business-hours line, the range 9-17 includes hour 17, so the last run is at 17:45, not 17:00. Use 9-16 if the work must stop before 17:00.

The day-of-month and day-of-week rule

When both day fields are restricted, meaning neither is *, the job runs on days that match either field, not both. The crontab manual states the rule, and it catches anyone writing "the first Monday of the month": 0 9 1-7 * 1 runs on the 1st to the 7th of every month and on every Monday. If only one of the two fields is restricted, only that one applies.

To get the first Monday, restrict one field in cron and test the other in the command:

0 9 1-7 * * [ "$(date +\%u)" -eq 1 ] && /usr/local/bin/report.sh

Cron starts this line on days 1 to 7, and the test lets it continue only when date +%u reports 1, meaning Monday. The backslash matters because cron turns an unescaped % into a new line. Implementations disagree on a few edge cases, such as a stepped day field like */2 combined with a weekday, so test those on the scheduler you actually use.

Shortcuts like @daily

Vixie cron, cronie and many other schedulers accept named shortcuts in place of the five fields:

Shortcut Same as Meaning
@yearly or @annually 0 0 1 1 * midnight on 1 January
@monthly 0 0 1 * * midnight on the 1st
@weekly 0 0 * * 0 midnight on Sunday
@daily or @midnight 0 0 * * * midnight every day
@hourly 0 * * * * the start of every hour
@reboot no equivalent once, when cron starts

They read well but are less portable, and they all fire at minute 0. On a busy server every @daily job then starts in the same second, so spreading jobs across odd minutes, such as 17 3 * * *, avoids the pile-up.

Six-field cron: seconds and years

Several schedulers add a field, which makes copied expressions a classic source of bugs:

Scheduler Fields Notes
Unix cron, Kubernetes, GitHub Actions 5, minute first the format above
Quartz (Java) 6 or 7, seconds first, optional year last day of week 1–7 with 1 = Sunday; ? in one day field
Spring @Scheduled 6, seconds first day of week 0–7, with 0 and 7 = Sunday
AWS EventBridge 6, minute first, year last ? in one day field

So "noon every day" is 0 12 * * * in a crontab, 0 0 12 * * ? in Quartz and cron(0 12 * * ? *) in EventBridge. Quartz also adds L for the last day, W for the nearest weekday and # for the nth weekday, as in 6#3 for the third Friday. Watch the weekday numbers when you translate: 1 is Monday in a crontab but Sunday in Quartz.

Time zones and daylight saving

Classic cron runs jobs in the time zone of the machine it runs on, and many servers and containers are set to UTC. A job written as 0 9 * * 1-5 on a UTC server runs at 9:00 UTC: 14:30 in Bengaluru, 10:00 in London during British Summer Time and 5:00 in New York during daylight saving time. Hosted schedulers such as GitHub Actions use UTC by default. Others let you choose: Kubernetes CronJobs have a timeZone field, and cronie reads a CRON_TZ variable.

If the scheduler follows a local zone with daylight saving, two nights a year are irregular. When clocks spring forward, an hour never happens, so a job at 30 2 * * * has no 2:30 that night in New York, Paris or Berlin. When clocks fall back, an hour happens twice. Vixie cron and its descendants run jobs from a skipped hour soon after the jump and do not repeat fixed-time jobs in a repeated hour, but other schedulers behave differently. The safe choice is to run schedulers in UTC, or to keep important jobs away from the clock-change hours, usually 1:00 to 4:00 local time. The World Clock & Time Zone Converter shows what a UTC schedule means in your city, and the time zone guide explains how offsets shift.

Mistakes that stop cron jobs running

When a job works in your terminal but not from cron, check these:

  • A minimal environment. Cron does not load your shell profile, and its PATH is short, often just /usr/bin:/bin. Use absolute paths.
  • Permissions. The script must be executable by the user the job runs as. The Chmod Calculator shows what modes such as 755 mean.
  • An unescaped percent sign. Cron turns % into a new line, which breaks commands such as date +%F. Write \%.
  • Lost output. Output is mailed to the user or discarded. Append >> /var/log/job.log 2>&1 to keep it.
  • Overlapping runs. If a job outlasts its interval, cron starts another copy anyway. Use a lock, such as flock on Linux.

Check a schedule with ArrayKit

The Cron Expression Explainer describes a five-field expression in a sentence. For 0 9 * * 1-5 it prints "Runs at minute 0, past hour 9, on every day-of-month, in every month, on Mon, Tue, Wed, Thu, Fri." and lists the next six run times in your browser's local time zone. It looks up to a year ahead, so an impossible date such as 0 0 30 2 * (30 February) shows no upcoming runs. It accepts numbers with *, ,, - and / in the standard ranges, including 7 for Sunday, and applies the either-day rule. Month and weekday names, @ shortcuts, six-field expressions and Quartz characters such as ? and L show an error, so translate them into the plain five-field form first.

To build an expression instead, the Cron Expression Generator sets each field to Every, Every N, Specific values or a Range, with names such as Jan and Mon on the buttons and numbers in the output. It shows the expression with a copy button, a plain-English summary and the next five runs, and has presets for common schedules. Both tools run in your browser, and nothing you type is uploaded.

Frequently asked questions

What is a cron expression? A cron expression is a schedule written as five fields: minute, hour, day of month, month and day of week. The scheduler checks it every minute and runs the job when the current time matches. Some schedulers add a sixth field for seconds or the year.

What time zone does cron use? Classic cron uses the time zone of the server it runs on, which is often UTC. Hosted schedulers such as GitHub Actions use UTC by default, while Kubernetes and cronie let you set a zone.

Why did my cron job run on the wrong day? Most likely both the day-of-month and day-of-week fields are set. When neither is an asterisk, cron runs the job on days that match either field, so a schedule meant for the first Monday runs on the 1st to the 7th and on every Monday.

Does cron support seconds? Standard crontab does not; its smallest unit is one minute. Quartz and Spring add a seconds field at the start, making six fields. In plain cron, a task that must run more than once a minute needs a loop in the script or a different tool, such as a systemd timer.

What does @daily mean in crontab? It is a shortcut for midnight every day: minute 0 and hour 0, with every other field left open. The other shortcuts are @hourly, @weekly, @monthly, @yearly and @reboot, which runs once when cron starts.

How do I run a cron job on the last day of the month? Standard cron has no symbol for it. The usual workaround is to schedule the job on days 28 to 31 and have the command check that tomorrow is the 1st. Quartz-style schedulers accept L in the day-of-month field instead.

Keep going

Cron Explainer

More guides

  • Date Calculations, Explained: Counting and Adding Days — How to count the days between two dates, add days, weeks or months to a date, and why calculators disagree: inclusive counts, month ends and leap years.
  • How Exact Age Is Calculated — How to calculate exact age in years, months and days: the borrow method, 29 February birthdays, total days and weeks, and why calculators differ by a day.
  • Unix Timestamps and Epoch Time, Explained — What a Unix timestamp is, why epoch time ignores leap seconds, how to tell seconds from milliseconds, converting in JS, Python and SQL, and the 2038 problem.
  • Time Zones, UTC and Daylight Saving, Explained — UTC vs GMT, offsets such as +05:30 and +05:45, IANA zone names, why EST and IST are ambiguous, daylight saving shifts, and using a time zone converter well.
  • UUIDs, Explained: v4, v7 and GUIDs — What a UUID is, how the 8-4-4-4-12 format works, UUID v4 vs v7 and the other versions, how unlikely a v4 collision is, and how UUIDs affect database indexes.
  • The Pomodoro Technique, Explained — How the Pomodoro Technique works: the 25-minute focus session, the 5-minute break, the long break after four, popular variations like 50/10, and tips that help.

All Date / Time tools · All guides