Web Development

What Is a Cron Expression? A Complete Guide for Developers

Learn everything about cron expressions, from basic syntax to advanced scheduling patterns. Understand how these five-field time strings automate tasks across Unix, Linux, and modern programming environments.

By Inventive HQ Team

A cron expression is a string of five space-separated fields that tells a scheduler exactly when to run a task: minute hour day-of-month month day-of-week. Each field accepts a number, a wildcard (* for "every value"), or a pattern built from special characters — so 30 14 * * 1-5 means "at 2:30 PM, Monday through Friday," and */15 * * * * means "every 15 minutes." Cron expressions began in Unix but are now the near-universal way to describe recurring schedules, from Linux crontab and Kubernetes CronJobs to AWS EventBridge, GitHub Actions, and language libraries in Node.js, Python, and Java.

That paragraph is the summary an AI overview gives you. The rest of this guide is the part it can't: the exact range of every field, what each special character actually does (including the ones that trip people up), the platform variations that silently break a copy-pasted expression, and a reference table of patterns you can lift directly. This is the pillar of our cron series — when you want to go deeper on one piece, we link the focused guides inline.

The five fields at a glance

Every standard cron expression is five fields, read left to right, moving from the smallest time unit to the weekday. An asterisk in any field means "every value."

The five fields of a cron expression Five boxes showing minute, hour, day of month, month, and day of week, each with its valid range, with a highlight that sweeps across the fields in turn. Anatomy of a cron expression: * * * * * Five fields, smallest time unit first. An asterisk means "every value." * Minute 0 - 59 * Hour 0 - 23 * Day of month 1 - 31 * Month 1 - 12 * Day of week 0 - 7 (Sun) Worked example 30 14 * * 1-5 minute 30 hour 14 (2 PM) any day any month Mon-Fri This expression means "Run at 2:30 PM every weekday"

Written as a pattern, the structure is:

<minute> <hour> <day-of-month> <month> <day-of-week>

And the classic ASCII diagram that ships in most crontab files:

┌───────────── minute (0 - 59)
│ ┌───────────── hour (0 - 23)
│ │ ┌───────────── day of month (1 - 31)
│ │ │ ┌───────────── month (1 - 12)
│ │ │ │ ┌───────────── day of week (0 - 7) (0 or 7 is Sunday)
│ │ │ │ │
* * * * *

If you want to practice turning expressions like this into plain English, our companion guide on how to read cron expressions walks through the field-by-field method step by step.

Field reference: exact ranges and allowed values

This is the table to bookmark. It covers every field, its numeric range, whether names are accepted, and the gotchas.

FieldPositionAllowed valuesNames accepted?Notes
Minute1st0-59No0-based
Hour2nd0-23No24-hour clock; 0 = midnight
Day of month3rd1-31No1-based; invalid dates (e.g. Feb 31) never fire
Month4th1-12JAN-DEC1-based (1 = January)
Day of week5th0-7SUN-SAT0 and 7 both = Sunday

Two traps live in this table. Minutes and hours are 0-based, but day-of-month and month are 1-based — there is no minute 60 and no month 0. And Sunday is both 0 and 7, a historical quirk so that ranges like MON-SUN (1-7) still work.

Special characters: the four you must know

The power of cron comes from four special characters that work inside any field. Master these and you can express almost any schedule.

CharacterNameMeaningExampleReads as
*AsteriskEvery value in the field* * * * *Every minute
,CommaA list of specific values0 9,12,15 * * *9 AM, noon, and 3 PM
-HyphenAn inclusive range0 9-17 * * *Every hour, 9 AM to 5 PM
/SlashA step ("every N")*/15 * * * *Every 15 minutes

You can combine them freely. */5 9-17 * * 1-5 uses a step, a range, and a range in one expression — "every 5 minutes, between 9 AM and 5 PM, Monday through Friday." Our deep dive on what */5 really means unpacks how step values interact with ranges, which is where most people's mental model breaks.

Advertisement

The question mark (?)

Some schedulers — notably Quartz and AWS — add a ? for the day-of-month or day-of-week field to mean "no specific value." It resolves the ambiguity of specifying one but not the other:

0 0 1 * ?          // 1st of every month (day of week doesn't matter)
0 0 ? * MON        // Every Monday (day of month doesn't matter)

Standard Vixie cron on Linux does not support ? — using it there is an error. This is a common source of "works on my scheduler, breaks on the server" bugs.

Macros: the @ shortcuts

Vixie cron (the implementation on nearly every Linux box) accepts named shortcuts that replace all five fields. They read better and eliminate off-by-one slips.

MacroEquivalentRuns
@yearly / @annually0 0 1 1 *Once a year, midnight Jan 1
@monthly0 0 1 * *Once a month, midnight on the 1st
@weekly0 0 * * 0Once a week, midnight Sunday
@daily / @midnight0 0 * * *Once a day, midnight
@hourly0 * * * *Once an hour, on the hour
@rebootOnce, when the cron daemon starts

@reboot is the odd one out: it is not time-based at all. It fires a single time when cron starts (typically at boot), which makes it handy for starting a long-running helper process. Not every scheduler supports these macros — cloud schedulers and language libraries vary — so confirm before relying on them.

Common patterns you can copy

Here is a reference of expressions that cover the schedules people actually need. Lift them directly.

ExpressionSchedule
* * * * *Every minute
*/5 * * * *Every 5 minutes
*/15 * * * *Every 15 minutes
0 * * * *Every hour, on the hour
0 */2 * * *Every 2 hours
0 0 * * *Daily at midnight
0 2 * * *Daily at 2 AM (common backup window)
30 14 * * 1-52:30 PM on weekdays
0 9-17 * * 1-5Every hour, 9 AM-5 PM, weekdays
*/15 9-17 * * 1-5Every 15 min during business hours, weekdays
0 9 * * 19 AM every Monday
0 0 * * 0Midnight every Sunday
0 0 1 * *Midnight on the 1st of each month
0 0 1,15 * *Midnight on the 1st and 15th
0 0 1 1,4,7,10 *Quarterly (1st of Jan, Apr, Jul, Oct)
0 0 1 1 *Once a year, midnight Jan 1

Want to build one for a schedule that isn't here? Use the interactive builder below — pick the values and it generates the expression with a plain-English translation and the next run times.

Loading interactive tool...

Build and validate visually

Cron Expression Builder lets you construct an expression field by field, see it translated to plain English instantly, and preview the next several execution times so you can confirm it does what you intend before you deploy it.

A simple example, decoded

Let's break down the expression from the diagram one more time:

30 14 * * 1-5

Reading from left to right:

  • 30: at 30 minutes past the hour
  • 14: at hour 14 (2:00 PM in 24-hour format)
  • *: every day of the month
  • *: every month
  • 1-5: Monday through Friday

This means "run at 2:30 PM every weekday" — the kind of schedule you'd use for a daily report generated each business afternoon.

Where cron came from

Cron traces back to the early Unix systems at Bell Labs; an early version is attributed to Ken Thompson, and the Version 7 Unix cron (around 1979) was written by Brian Kernighan. The name comes from chronos, the Greek word for time. That early daemon woke up once a minute, read a single system-wide table of jobs, and ran whatever was due.

The version almost everyone actually runs today is Vixie cron, written by Paul Vixie in 1987. Vixie's key addition was per-user crontabs — letting ordinary users schedule their own jobs without root — plus the @ macros above. By 1992 the crontab format was standardized in POSIX. If you want the practical difference between system-wide and per-user schedules, see our guide on the system crontab vs. user crontab.

Platform variations that break copy-paste

The five-field format is the baseline, but several major platforms diverge. Copying an expression between them is the single most common way cron schedules silently misfire.

PlatformFieldsFormatNotable difference
Vixie cron (Linux)5min hour dom mon dowBaseline; supports @ macros, not ?
Quartz (Java)6-7sec min hour dom mon dow [year]Seconds field first; requires ? in one day field
AWS EventBridge6min hour dom mon dow yearYear is required; uses ?; no @ macros
Kubernetes CronJob5min hour dom mon dowStandard 5-field; per-job time zone supported
Spring @Scheduled6sec min hour dom mon dowSeconds field first

The takeaway: always check whether your scheduler expects five, six, or seven fields before pasting an expression. A five-field 0 0 * * * pasted into Quartz shifts every field one place to the left and schedules something entirely different.

Day of month vs. day of week: the OR rule

One subtlety catches even experienced developers. In standard Vixie cron, when both the day-of-month and day-of-week fields are restricted (neither is *), the job runs on days matching either condition — not both:

0 0 1 * 5  // Runs on the 1st of every month OR every Friday

If you expected "the 1st, but only if it's a Friday," this expression does the opposite of what you want. To get an AND-style condition you generally need application logic or a scheduler that supports it explicitly.

Common mistakes

  • Using 7 for Sunday when your code expects 0 — both are valid in most implementations, but be consistent to avoid confusion.
  • Forgetting the 0-based/1-based split — minutes and hours start at 0; days and months start at 1.
  • Ignoring the server time zone — cron uses the server's local time, not the user's. Standardize on UTC for distributed systems.
  • Assuming sub-minute precision — standard cron cannot schedule to the second; one minute is the floor.
  • Pasting a five-field expression into a six-field scheduler — every field shifts and the schedule changes silently.
  • Using ? on Linux — that's a Quartz/AWS feature; Vixie cron rejects it.

Best practices

Test before deploying. Use a validator or builder to confirm your expression and preview the next run times. A wrong asterisk can turn "once a day" into "every minute."

Standardize on UTC. Running servers on UTC sidesteps the daylight-saving edge cases where a job runs twice or gets skipped.

Document each schedule. Cron is compact but not self-explanatory; a one-line comment above each entry saves future debugging.

Don't over-schedule. * * * * * runs 1,440 times a day — make sure that frequency is genuinely needed, and ensure runs can't overlap.

Plan for failure. Jobs can fail or run long. Add logging, alerting, and a guard against a slow run colliding with the next one.

Cron in application code

Beyond the operating system, nearly every language and platform speaks cron: node-cron and cron in Node.js, APScheduler in Python, Quartz and Spring's @Scheduled in Java, Kubernetes CronJobs, GitHub Actions workflows, and cloud schedulers like AWS EventBridge and Google Cloud Scheduler. The syntax knowledge transfers, but the field count and edge cases don't always — so for the patterns and pitfalls specific to running cron inside an app, see using cron expressions in applications.

Getting started

The fastest way to internalize cron is to build a few expressions and read them back. Start with the common patterns above, then reach for the special characters as your schedules get more specific. Use the Cron Expression Builder to create and validate expressions visually, with instant plain-English translations so you know exactly when your tasks will run.

Whether you're scheduling backups, automating reports, or orchestrating complex workflows, mastering these five fields unlocks powerful automation that saves hours and ensures critical tasks never get forgotten.

Frequently Asked Questions

What is a cron expression?

A cron expression is a compact string of five fields separated by spaces that tells a scheduler when to run a task. The fields, in order, are minute (0-59), hour (0-23), day of month (1-31), month (1-12), and day of week (0-7, where both 0 and 7 mean Sunday). An asterisk in a field means "every value," so * * * * * runs every minute and 30 14 * * 1-5 runs at 2:30 PM on weekdays. Some systems add a sixth field for seconds (Quartz) or a year field (AWS).

What are the five fields of a cron expression in order?

From left to right: minute, hour, day of month, month, and day of week. A useful way to remember it is that the fields move from the smallest time unit to the largest, then finish with the weekday. So 0 0 1 1 * reads as minute 0, hour 0, day-of-month 1, month 1 (January), any weekday — meaning midnight on January 1st every year.

What does the asterisk mean in a cron expression?

An asterisk (*) means "every valid value" for that field. * * * * * means every minute of every hour of every day. 0 * * * * means the top of every hour, because the minute is fixed at 0 but every other field is a wildcard. The asterisk is what lets a five-character string cover thousands of run times without listing them.

What does */5 mean in cron?

*/5 in the minute field means "every 5 minutes" — it is a step value applied to the whole range. So */5 * * * * fires at :00, :05, :10, and so on through :55, twenty-four hours a day. The slash reads as "every N," and it can be combined with a range: */5 9-17 * * 1-5 runs every 5 minutes, but only between 9 AM and 5 PM on weekdays.

What is the difference between the five-field and six-field cron format?

The classic Unix/Vixie format has five fields (minute through day of week). Six-field formats add either seconds at the front or a year at the end, depending on the platform. Quartz Scheduler (Java) uses second minute hour day-of-month month day-of-week [year], while AWS EventBridge uses minute hour day-of-month month day-of-week year. Copying a five-field expression into a six-field scheduler (or vice versa) is one of the most common cron mistakes.

What are cron macros like @daily and @reboot?

Vixie cron supports named shortcuts that replace the five fields entirely: @yearly (or @annually) equals 0 0 1 1 *, @monthly equals 0 0 1 * *, @weekly equals 0 0 * * 0, @daily (or @midnight) equals 0 0 * * *, and @hourly equals 0 * * * *. @reboot is special — it runs once when the cron daemon starts rather than on a time schedule. Not every scheduler supports these, so check your platform's docs.

How do you read the cron expression 30 14 * * 1-5?

It means "at 2:30 PM, Monday through Friday." The 30 is the minute, 14 is the hour in 24-hour time (2 PM), the two asterisks mean every day of the month and every month, and 1-5 restricts it to weekdays (Monday is 1, Friday is 5). It is a typical schedule for a weekday afternoon report.

What time zone does cron use?

Standard Unix cron uses the server's local time zone, not the user's. This matters most around daylight saving transitions, when a job scheduled for 2:30 AM might run twice or be skipped. For distributed systems, the common fix is to run servers on UTC so schedules stay stable year-round. Some modern schedulers (Kubernetes CronJobs, AWS EventBridge) let you set an explicit time zone per job.

Can cron run a job every 30 seconds?

Not with standard five-field cron — its minimum granularity is one minute, so you cannot express sub-minute schedules directly. Common workarounds are to run a job every minute and have it sleep 30 seconds before a second execution, use a six-field scheduler that supports a seconds field (like Quartz), or use a purpose-built interval scheduler such as systemd timers with OnUnitActiveSec.

cronautomationtask schedulinglinuxdevops