How to Read Cron Expressions: The 5 Fields, the 4 Special Characters, and a Cheat Sheet

CodexFuse Team · 2026-11-06T10:52:57.405Z · 17 min read min read

How to Read Cron Expressions: The 5 Fields, the 4 Special Characters, and a Cheat Sheet

A cron expression is a short string of five numbers and symbols that tells a computer when to run a job. Learning how to read cron expressions takes about ten minutes once you know the field order, and this guide decodes them field by field for beginners running jobs on a server. It covers the cron 5 fields, the four special characters, the two traps that catch almost everyone, and a cheat sheet of the schedules you will actually use, as of November 2026.

Every expression in this guide follows the standard five-field Unix format, and every example was checked against the crontab manual and the site's own tools before publication. When you want to test an expression of your own as you read, paste it into our free cron expression generator and read the plain-English description it returns. The next section starts with the definition, then the fields, then the characters, then the practice.

What Is a Cron Expression?

A cron expression is a string of five space-separated fields that defines a repeating schedule: minute, hour, day of the month, month, and day of the week. The scheduler checks the current time against each field, and when all five fields match, the job runs. The smallest unit a standard cron expression can express is one minute, because there is no seconds field.

Cron expressions show up far beyond the Linux crontab where they started. The same five fields schedule GitHub Actions workflows, Kubernetes CronJobs, and cloud schedulers, which is why one guide covers all of them. The format is deliberately small: five fields, four special characters, and a handful of shortcuts cover nearly every schedule you will ever write.

This guide is a cron expression explained in plain English, with no assumed knowledge. Each section answers one question, and the cheat sheet near the end collects every schedule in one table you can keep.

The 5 Fields of a Cron Expression, in Order

Every standard cron expression reads left to right in the same order: minute, hour, day of the month, month, day of the week. Memorize that order once — minute, hour, day, month, weekday — and every expression becomes a sentence you can read aloud. The table below gives each field's allowed values.

FieldValuesWhat it means
Minute0–59The minute of the hour
Hour0–23The hour, on a 24-hour clock (0 is midnight, 12 is noon, 23 is 11 PM)
Day of month1–31The day of the month
Month1–12 or JAN–DECThe month, as a number or a three-letter name
Day of week0–7 or SUN–SATThe day of the week (0 and 7 are both Sunday)

Two details in that table cause most of the confusion. First, the day of the week counts Sunday as both 0 and 7, so 0 and 7 mean the same day, while strict POSIX cron only allows 0–6. Second, month and weekday names use the first three letters and are case-insensitive, so JAN, Jan, and jan all mean January, but ranges and lists of names are not allowed.

A simple way to picture the fields is the diagram below, which appears in most cron guides because it makes the order visible at a glance. Read it top to bottom, left to right, and each field falls into place.

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

The 4 Special Characters That Do All the Work

Numbers alone can only express fixed times, so cron adds four special characters. Together they are the whole vocabulary: an asterisk for every value, a comma for lists, a hyphen for ranges, and a slash for steps. These are the cron special characters, and they combine freely inside any field.

CharacterNameMeaningExample
*AsteriskEvery value in the field* in the minute field means every minute
,CommaA list of values1,15 in the day field means the 1st and the 15th
-HyphenAn inclusive range9-17 in the hour field means 9 AM through 5 PM
/SlashA step through a range*/15 in the minute field means every 15 minutes

The slash is the one people misread most often. A step counts from the start of the range, not from the moment you created the job, so */45 in the minute field fires at minute 0 and minute 45 of every hour, not once every 45 minutes. The same rule makes */7 fire at minutes 0, 7, 14, 21, 28, 35, 42, 49, and 56, then jump back to 0 with a four-minute gap across the hour.

Characters compose: 0,30 9-17 * * 1-5 means at minutes 0 and 30, during hours 9 through 17, Monday through Friday — every half hour of the working day on weekdays. Once you can read that sentence, you can read any expression, because every expression is the same trick repeated five times.

How to Read Any Cron Expression in 4 Steps

Reading an unfamiliar expression is a mechanical process, not a memory test. Work through the four steps below in order, and the meaning assembles itself. Try them on the worked example at the end of the list.

  1. Split the expression into its five fields, left to right: minute, hour, day of month, month, day of week.
  2. Ask of each field: is it a fixed number, a list, a range, a step, or a wildcard?
  3. Translate each field into plain words, keeping the field order.
  4. Join the five translations into one sentence, and check the sentence against a tool before you deploy it.

Take 30 2 * * 1-5: minute 30 means half past the hour, hour 2 means 2 AM, day of month and month are wildcards, and day of week 1-5 means Monday through Friday. The sentence is: at 2:30 AM, Monday through Friday. If the minute had been an asterisk instead, the same expression would run 60 times between 2 AM and 3 AM.

Cron Expression Examples, Decoded

These cron examples cover the schedules beginners meet most often. Read each row left to right, and check your reading against the plain-English column before moving on.

ExpressionPlain English
0 9 * * 1-5Weekdays at 9:00 AM
*/15 * * * *Every 15 minutes, at :00, :15, :30, and :45
0 0 1 * *At midnight on the 1st of every month
30 2 * * 0At 2:30 AM every Sunday
0 12 1,15 * *At noon on the 1st and 15th of every month
0 0 * * 0At midnight every Sunday
15 3 * * 1-5At 3:15 AM, Monday through Friday
0 0 29 2 *At midnight on February 29 — only in leap years, so in other years it never fires
0 0 * * *Every day at midnight
0 0 1 1 *Once a year, at midnight on January 1
* * * * *Every minute of every hour of every day — 1,440 runs a day
*/5 * * * *Every 5 minutes

The February 29 row deserves a second look. A day-of-month value that does not exist in a given month simply never matches, so 0 0 29 2 * fires only in leap years — the next ones are 2028 and 2032. The same rule is why an expression asking for February 30 would never run at all.

Count the runs before you trust a schedule. The Python snippet below computes the numbers behind the table, and its output was verified by running the code for this guide.

import calendar
from datetime import date

# 1. Runs in one day for * * * * *
print(24 * 60)  # 1440

# 2. Runs in the midnight hour for * 0 * * *
print(60)  # 60

# 3. Runs in 2026 for 0 0 13 * 5 (Fridays + 13ths - Friday the 13ths)
fridays = sum(1 for m in range(1, 13)
              for d in range(1, calendar.monthrange(2026, m)[1] + 1)
              if date(2026, m, d).weekday() == 4)
fri_13 = sum(1 for m in range(1, 13) if date(2026, m, 13).weekday() == 4)
print(fridays + 12 - fri_13)  # 61

# 4. Run dates in October 2026 for 0 0 13 * 5
print(sorted(set([d for d in range(1, 32)
                  if date(2026, 10, d).weekday() == 4] + [13])))
# [2, 9, 13, 16, 23, 30]

# 5. Next Feb 29 years after 2026
print([y for y in range(2026, 2041) if calendar.isleap(y)])
# [2028, 2032, 2036, 2040]

The Cheat Sheet: Schedules You Will Actually Use

This cron expression cheat sheet collects the schedules that cover most real jobs. Copy the expression, check the meaning, and adjust the hour to your own server's clock.

ScheduleExpression
Every minute* * * * *
Every 5 minutes*/5 * * * *
Every 15 minutes*/15 * * * *
Every hour, on the hour0 * * * *
Every day at midnight0 0 * * *
Every day at 2:30 AM30 2 * * *
Every weekday at 9 AM0 9 * * 1-5
Every Monday at 8:30 AM30 8 * * 1
First of every month at midnight0 0 1 * *
Every Sunday at midnight0 0 * * 0
Twice a day, 6 AM and 6 PM0 6,18 * * *
Every January 1 at midnight0 0 1 1 *
Quarterly, on the 1st of Jan, Apr, Jul, Oct0 0 1 1,4,7,10 *

The 2:30 AM daily backup is the classic cron job for a reason: it runs when nobody is using the server, and it avoids the top of the hour, when many other scheduled jobs fire at once. GitHub Actions documents the same advice for its own schedules, because high-load times include the start of every hour.

The Day-of-Month vs Day-of-Week Trap

Here is the trap that catches almost everyone. When both the day-of-month and day-of-week fields are restricted — neither is an asterisk — the job runs when either field matches, not when both do. The manual states it plainly: the command runs when at least one of the two day fields matches the current time.

The famous example is 0 0 13 * 5, which looks like Friday the 13th but is not. It runs at midnight on the 13th of every month and at midnight every Friday, which came to 61 runs in 2026: 52 Fridays plus 12 thirteenths, minus the 3 days that were both. In October 2026 alone it fired on the 2nd, 9th, 13th, 16th, 23rd, and 30th — five Fridays plus the 13th.

The man page's own example makes the same point: 30 4 1,15 * 5 runs at 4:30 AM on the 1st and 15th of each month, plus every Friday. If you genuinely need the first Monday of the month, restrict only the weekday field and add a guard inside the script, such as a date check that the day of the month is 7 or less. One nuance worth knowing: in Vixie cron and cronie, a day field that starts with an asterisk, such as */2, counts as unrestricted, so the two day fields combine with an AND instead of the usual OR.

The rule to remember is simple: restrict one day field at a time, and put the other condition inside the job. Anything else is a schedule that fires on days you did not expect.

There Is No Timezone in a Cron Expression

A cron expression has no timezone field. The scheduler reads the host's clock and fires whenever the local time matches, so 0 9 * * * means 9 AM in the server's timezone, not yours. Most cloud servers default to UTC, so the same expression means 9 AM UTC on a fresh VPS and 9 AM Eastern on a laptop set to New York time.

Check the server's clock with the date command before you trust any schedule, and remember that cron runs on your server — our VPS hosting guide explains the server setup this scheduling assumes. The TZ variable you can set inside a crontab changes only the timezone the job's own commands see; it does not move the time the job fires. Some Linux cron implementations add a CRON_TZ variable that does move the evaluation timezone, but BSD and macOS cron do not honour it, so never rely on it for a portable schedule.

Daylight saving time adds one more wrinkle: a job set for 2:30 AM may skip entirely or shift to 3:00 AM when the clocks spring forward, and a 1:00 AM job may run twice when they fall back. GitHub Actions documents the spring-forward shift for its own schedules, and the Quartz tutorial warns of the same skip-or-repeat risk. If a job must run exactly once a day, schedule it well away from 2 AM.

The @ Shortcuts That Replace All Five Fields

Vixie cron, the version that ships on most Linux systems, accepts eight shortcut strings that replace all five fields. They are handy for the schedules everyone writes, and they read exactly like English.

ShortcutMeansEquivalent expression
@rebootRun once, at startup(no equivalent)
@yearly / @annuallyOnce a year0 0 1 1 *
@monthlyOnce a month0 0 1 * *
@weeklyOnce a week0 0 * * 0
@daily / @midnightOnce a day0 0 * * *
@hourlyOnce an hour0 * * * *

Two warnings come with the shortcuts. They are not universal: GitHub Actions does not support them, and neither do Quartz or AWS EventBridge, so an expression that works in your crontab will fail in a workflow file. And @reboot is the odd one out — it runs once when the machine starts, not on a repeating schedule, so never use it for a job that must repeat.

When 5 Fields Become 6: Quartz and Other Dialects

Everything so far is the standard five-field format, and most platforms use it. Quartz is the exception: Quartz adds a seconds field first, so its expressions have six or seven fields, with an optional year at the end. A five-field crontab line pasted into Quartz is read seconds-first and fires at the wrong time, which is why the two formats are never interchangeable.

Quartz also numbers the days of the week differently — 1 is Sunday, not 0 — and adds four characters standard cron does not have: ? for no specific value, L for last, W for the nearest weekday, and # for the nth weekday of the month. Those characters belong to Quartz and a few cloud schedulers, and a Linux crontab rejects an expression that uses them. AWS EventBridge also uses a six-field format with a year field, and it uses ? in one of the day fields instead of an asterisk.

GitHub Actions stays with the standard five fields and runs schedules in UTC by default, but it accepts an optional IANA timezone key on each schedule entry, so a workflow can run at 5:30 AM New York time without moving the server clock. Its shortest interval is five minutes, and its schedules run only from the default branch. The lesson is the same for every platform: read the platform's own docs before you paste an expression into it, because the field count is the part that changes.

The 5 Mistakes That Break Cron Jobs

Most broken cron jobs fail for one of five reasons, and all five are avoidable. Check your expression against this list before you save it.

  1. Leaving the minute as an asterisk. * 0 * * * was meant to run once at midnight, but it runs 60 times in the midnight hour — once for every minute.
  2. Restricting both day fields. 0 0 13 * 5 is not Friday the 13th; it is the 13th and every Friday, as the trap section showed.
  3. Using Quartz characters in a Linux crontab. L, W, # and ? are not portable, and a standard crontab rejects them outright.
  4. Pasting a five-field expression into a six-field scheduler. Quartz reads the first field as seconds, so the job fires at the wrong time.
  5. Trusting your shell's environment. Cron runs jobs with a minimal environment, so a script that works by hand can fail in cron because its PATH or variables are missing — always use full paths and log the output.

Verify Before You Deploy

Reading an expression correctly is half the job; the other half is proving it before it runs on your server. Paste the expression into a plain-English explainer and read the next few run times, not just the description — our free crontab explainer translates any five-field expression into plain English, and our cron expression parser breaks it into its fields so you can check each one. The well-known crontab.guru does the same job online, and GitHub's own docs point readers to it.

If the job runs inside a scheduled container rather than directly on the host, the same five fields apply — the scheduler just lives inside the container, so our Docker beginner guide covers the setup this assumes. Verify every schedule the same way, every time: a wrong expression is silent, and the first sign of trouble is usually a job that ran 60 times or never ran at all.

Sources

  1. OpenBSD crontab(5) manual page — https://man.openbsd.org/OpenBSD-6.5/crontab.5 — five fields, ranges, steps, lists, the day-field OR rule, and the @ shortcuts.
  2. Quartz Scheduler CronTrigger Tutorial — https://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html — six- and seven-field format, ?, L, W, # characters, and worked examples.
  3. GitHub Docs, "Events that trigger workflows" (schedule) — https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#schedule — five fields, UTC default, timezone key, five-minute minimum, and the crontab.guru recommendation.
  4. MakeMyDev, "Cron expressions explained in plain English" — https://www.makemydev.com/blog/cron-expressions-explained — field-by-field guide, common schedules, and the unpinned-minute mistake.
  5. crontab.guru — https://crontab.guru/ — plain-English cron expression editor, owned by Cronitor.
  6. Jagodana, "How to Read and Write Cron Expressions Without Memorising the Syntax" — https://www.jagodana.com/blogs/introducing-cron-job-calculator — field-by-field decodes and the day-field OR example.
  7. Ponderloop, "Cron expressions explained" — https://www.ponderloop.com/learn/cron-expressions-explained/ — step-value behaviour, including */45 firing at :00 and :45.
  8. DevCraftKit, "Why day-of-month and day-of-week behave differently in cron" — https://www.devcraftkit.com/blog/why-day-of-month-and-day-of-week-behave-differently-in-cron — October 2026 run dates for 0 0 13 * 5.
  9. Toolbox-Kit, "Best Free Cron Expression Generators" — https://www.toolbox-kit.com/blog/best-free-cron-expression-generators-crontab-guru-alternatives — crontab.guru feature comparison.
  10. Cronbase, "Cron timezone guide" — https://cronbase.dev/guides/cron-timezone-guide — no timezone stored inside a cron expression; cloud VMs default to UTC.

Frequently Asked Questions

What does 0 9 * * 1-5 mean?

It means weekdays at 9:00 AM. Reading left to right: minute 0, hour 9, any day of the month, any month, and days 1 through 5, which are Monday through Friday.

How do I read a cron expression I have never seen before?

Split it into its five fields — minute, hour, day of month, month, day of week — and translate each field into plain words, then join them into one sentence. If the sentence still looks wrong, paste the expression into a plain-English explainer such as the crontab explainer on this site and compare the two readings.

What is the difference between * and */5 in the minute field?

An asterisk means every value, so * in the minute field runs the job every minute, or 1,440 times a day. The step */5 means every fifth minute starting from 0, so the job runs at :00, :05, :10, and so on through :55 — twelve times an hour.

Does a cron expression include a timezone?

No. A cron expression carries no timezone information, so the scheduler uses the clock of the machine it runs on, and most cloud servers default to UTC. Check the server's clock before you trust a schedule, and use the platform's own timezone setting when one exists.

Why did my cron job run on days I did not expect?

The usual cause is the day-field rule: when both the day-of-month and day-of-week fields are restricted, the job runs when either one matches. Check whether both day fields are set, and if they are, restrict only one and move the other condition into the script.

Featured on Twelve Tools Hunt for Tools Featured on LaunchBuff Launch Llama Tools Launch your MCP Top Web Development Companies in the US - OnToplist.com Designer Listings Listed on SeeWhatNewAI Featured on SubmitMySaas Featured on AI Agents Directory