Back to Blog
·Cron Crew Team

Cron Expression Syntax Explained Simply

Cron expressions look cryptic at first. Five asterisks somehow translate into 'every weekday at 9 AM.' Here's how to read, write, and debug cron expressions.

Cron Expression Syntax Explained Simply

Cron Expression Syntax Explained Simply

Cron expressions look cryptic at first glance. Five asterisks and numbers separated by spaces somehow translate into "run this job every weekday at 9 AM." If you have ever stared at 0 9 * * 1-5 and wondered what it means, this guide is for you.

We will break down cron expression syntax from the basics to advanced patterns, with plenty of examples along the way. By the end, you will be able to read, write, and debug cron expressions with confidence. Once you have your expressions ready, you can set up cron monitoring in 5 minutes to ensure your jobs run as expected.

Cron Expression Basics

A standard cron expression consists of five fields separated by spaces. Each field represents a unit of time, and together they specify when a job should run.

The Five Fields

* * * * * | | | | | | | | | +--- Day of week (0-6, Sunday = 0) | | | +----- Month (1-12) | | +------- Day of month (1-31) | +--------- Hour (0-23) +----------- Minute (0-59)

Reading from left to right: minute, hour, day of month, month, day of week.

A Simple Example

Let us decode 30 14 * * *:

  • 30 - At minute 30
  • 14 - Of hour 14 (2 PM in 24-hour time)
  • * - Every day of the month
  • * - Every month
  • * - Every day of the week

This expression means: "Run at 2:30 PM every day."

Time is Usually UTC

An important note: most cron systems run in UTC (Coordinated Universal Time) unless configured otherwise. A job scheduled for 0 9 * * * runs at 9 AM UTC, not 9 AM in your local timezone. Always check what timezone your cron daemon is using to avoid surprises.

The Five Fields Explained

Let us look at each field in detail.

┌───────────── minute (0-59) │ ┌───────────── hour (0-23) │ │ ┌───────────── day of month (1-31) │ │ │ ┌───────────── month (1-12) │ │ │ │ ┌───────────── day of week (0-6, Sunday=0) │ │ │ │ │ * * * * *

Minute (0-59)

The first field specifies which minute of the hour the job runs. Valid values are 0 through 59.

  • 0 - At the top of the hour (minute 0)
  • 30 - At half past the hour
  • 45 - At 45 minutes past the hour

Hour (0-23)

The second field specifies which hour of the day. Uses 24-hour format.

  • 0 - Midnight
  • 12 - Noon
  • 23 - 11 PM

Day of Month (1-31)

The third field specifies which day of the month. Valid values are 1 through 31.

  • 1 - First of the month
  • 15 - Middle of the month
  • 31 - Last day (only runs in months with 31 days)

Month (1-12)

The fourth field specifies which month. Valid values are 1 through 12.

  • 1 - January
  • 6 - June
  • 12 - December

Day of Week (0-6)

The fifth field specifies which day of the week. On most systems, 0 is Sunday.

  • 0 - Sunday
  • 1 - Monday
  • 5 - Friday
  • 6 - Saturday

Special Characters

Cron expressions support special characters that give you flexibility in scheduling.

Asterisk (*) - Every Value

The asterisk means "every" or "any." It matches all possible values for that field.

* * * * * # Every minute of every hour of every day 0 * * * * # Every hour (at minute 0) 0 0 * * * # Every day at midnight

Comma (,) - Multiple Values

Use commas to specify multiple specific values.

0 9,17 * * * # At 9 AM and 5 PM daily 0 0 1,15 * * # On the 1st and 15th of each month 0 0 * * 1,3,5 # On Monday, Wednesday, and Friday

Hyphen (-) - Range of Values

Use hyphens to specify a range of consecutive values.

0 9-17 * * * # Every hour from 9 AM to 5 PM 0 0 1-7 * * # First 7 days of each month 0 0 * * 1-5 # Monday through Friday

Slash (/) - Step Values

Use slashes to specify step intervals. The format is start/step or */step for "every N."

*/5 * * * * # Every 5 minutes */15 * * * * # Every 15 minutes 0 */2 * * * # Every 2 hours (at minute 0) 0 0 */3 * * # Every 3 days

You can also combine with ranges:

0 9-17/2 * * * # Every 2 hours between 9 AM and 5 PM (9, 11, 1, 3, 5)

Common Examples with Explanations

Here are expressions you will encounter and use frequently.

Every Minute

* * * * *

Runs every single minute. Useful for testing but rarely appropriate for production.

Every 5 Minutes

*/5 * * * *

The /5 means "every 5th minute." Runs at 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, and 55 minutes past each hour.

Every Hour

0 * * * *

Runs at minute 0 of every hour. Note that * * * * * would run every minute, not every hour.

Daily at Midnight

0 0 * * *

Runs at 00:00 (midnight) every day. One of the most common patterns for daily maintenance tasks.

Daily at a Specific Time

30 4 * * *

Runs at 4:30 AM daily. Common for backups that should complete before business hours.

Weekdays at 9 AM

0 9 * * 1-5

Runs at 9 AM Monday through Friday. The 1-5 in the day-of-week field covers Monday (1) through Friday (5).

Every Sunday at 4:30 AM

30 4 * * 0

Runs at 4:30 AM every Sunday. Sunday is 0 in most cron implementations.

First of Each Month

0 0 1 * *

Runs at midnight on the first day of every month. Common for monthly reports or billing tasks.

Quarterly (First Day of Quarter)

0 0 1 1,4,7,10 *

Runs at midnight on January 1, April 1, July 1, and October 1.

Weekday Gotchas

The day-of-week field causes the most confusion. Here are the common pitfalls.

Sunday Can Be 0 or 7

On most standard cron implementations, Sunday is 0. However, some systems (notably some versions of Vixie cron and newer systems) also accept 7 as Sunday.

0 0 * * 0 # Sunday (standard) 0 0 * * 7 # Sunday (on some systems)

For maximum compatibility, use 0 for Sunday.

Monday Through Friday

0 9 * * 1-5

This is the standard way to specify weekdays. Monday is 1, Friday is 5.

Weekend Only

0 9 * * 0,6

Sunday (0) and Saturday (6). Note the comma separating the two values.

Be Careful with Day-of-Month and Day-of-Week

When you specify both day-of-month and day-of-week (non-asterisk values in both fields), most cron implementations run the job when EITHER condition is met, not when both are met.

0 0 15 * 1 # Runs on the 15th AND on every Monday

This runs on every Monday AND on the 15th of every month, not just on Mondays that fall on the 15th.

Month and Day Names

Some cron implementations allow three-letter abbreviations for months and days.

Month Names

0 0 1 JAN * # January 1st 0 0 1 JAN,APR,JUL,OCT * # Quarterly

Day Names

0 9 * * MON # Every Monday 0 9 * * MON-FRI # Weekdays

Recommendation: Use Numbers

While names can be more readable, numbers are more portable across different cron implementations. If you are writing cron expressions that might be used on different systems, stick with numbers.

Advanced Patterns

Once you are comfortable with the basics, you can create more complex schedules.

Every 2 Hours

0 */2 * * *

Runs at the top of every even-numbered hour (0:00, 2:00, 4:00, etc.).

Every Hour During Business Hours

0 9-17 * * *

Runs at minute 0 of each hour from 9 AM to 5 PM.

Every Other Day

0 0 */2 * *

Runs at midnight every other day (1st, 3rd, 5th, etc. of the month).

Specific Days

0 0 * * 1,4

Runs at midnight on Mondays and Thursdays only.

Twice Daily

0 9,21 * * *

Runs at 9 AM and 9 PM daily.

Every 10 Minutes During Business Hours

*/10 9-17 * * 1-5

Runs every 10 minutes, but only between 9 AM and 5 PM on weekdays.

Last Day of Month (Approximation)

Cron does not have a "last day of month" syntax. A common workaround:

0 0 28-31 * *

This runs on the 28th through 31st, so it hits the last day of every month. However, it also runs on other days. A better approach is to check the date in your script:

0 0 28-31 * * [ "$(date +\%d -d tomorrow)" = "01" ] && /path/to/script.sh

Common Mistakes

Avoid these frequent errors when writing cron expressions.

Forgetting That Some Fields Are 0-Indexed

Hours and minutes start at 0, but days of month and months start at 1. Days of week start at 0 (Sunday).

0 0 0 * * # WRONG: Day 0 doesn't exist 0 0 1 * * # CORRECT: First day of month

Timezone Confusion

Your cron daemon probably runs in UTC. A job at 0 9 * * * runs at 9 AM UTC, which might be 4 AM or 2 AM in your local timezone depending on where you are and whether daylight saving time is in effect.

Always confirm your cron daemon's timezone and account for it in your expressions.

Day-of-Week vs Day-of-Month Conflict

As mentioned earlier, specifying both creates an OR condition, not an AND condition.

# Intended: 15th of month if it's a Monday # Actual: 15th of month OR any Monday 0 0 15 * 1

If you need both conditions to be true, handle the logic in your script instead.

Wildcard Overuse

Using asterisks everywhere creates jobs that run very frequently:

* * * * * # Every minute - are you sure?

Always double-check that your expression does not run more often than intended. For more guidance on avoiding scheduling pitfalls, see our cron job best practices for reliability.

Testing Your Expressions

Before deploying a cron expression to production, verify it does what you expect.

Use a Cron Expression Validator

Online tools can show you when your expression will next run. Enter your expression and see the next 5-10 scheduled times. If they do not match your expectations, adjust and test again.

Check "Next N Runs" Before Deploying

When you set up a cron job, look at the calculated next runs:

  • Next run: 2026-01-24 00:00:00 UTC
  • Following run: 2026-01-25 00:00:00 UTC
  • Then: 2026-01-26 00:00:00 UTC

If these times match your intention, you are good to deploy.

Watch for Edge Cases

Test expressions around edge cases:

  • Month boundaries (end of January, end of February)
  • Daylight saving time transitions
  • Leap years

Quick Reference Table

Here is a handy reference for common schedules:

ScheduleExpression
Every minute* * * * *
Every 5 minutes*/5 * * * *
Every 15 minutes*/15 * * * *
Every 30 minutes*/30 * * * *
Every hour0 * * * *
Every 2 hours0 */2 * * *
Every day at midnight0 0 * * *
Every day at 3 AM0 3 * * *
Every day at 9 AM and 5 PM0 9,17 * * *
Every weekday at 9 AM0 9 * * 1-5
Every Sunday at midnight0 0 * * 0
Every Saturday at 2:30 AM30 2 * * 6
First of month at midnight0 0 1 * *
Every Monday at 9 AM0 9 * * 1
Twice a month (1st and 15th)0 0 1,15 * *
Every quarter (Jan, Apr, Jul, Oct 1st)0 0 1 1,4,7,10 *
Yearly (January 1st)0 0 1 1 *

Conclusion

Cron expressions are a powerful way to specify schedules, but their compact syntax can be confusing at first. Remember the five fields (minute, hour, day of month, month, day of week), learn the special characters (asterisk, comma, hyphen, slash), and always test your expressions before deploying.

Key takeaways:

  • Read expressions left to right: minute, hour, day, month, weekday
  • Asterisk means "every," comma means "and," hyphen means "through," slash means "every Nth"
  • Watch out for timezone differences, usually UTC
  • Be careful with day-of-month and day-of-week together (OR logic, not AND)
  • Always validate expressions before deploying to production

When you set up cron monitoring with Cron Crew, you can enter your cron expression and see exactly when your job is expected to run. This validation helps catch expression mistakes before they cause missed runs or unexpected behavior. For a comprehensive overview of monitoring strategies, read our complete guide to cron job monitoring. And when you are ready to compare tools, check out our cron monitoring pricing comparison. Sign up for a free account and try it with your next scheduled job.