How to Write Cron Expressions: The 5 Fields, Step Values and the Day-of-Month Trap
Almost every recurring job on the internet — the nightly backup, the Monday report, the every-fifteen-minutes health check — is described by a single short string called a cron expression. It looks cryptic the first time you see it and obvious forever after you learn it, because it is just five numbers separated by spaces. Our cron expression generator removes the remaining friction: pick a mode for each field — every value, every N, specific values, or a range — press Generate Cron Expression, and you get the expression plus a plain-language reading and a preview of the next 5 run times. This guide explains the five fields, the four ways to fill each one, a worked set of schedules, and the two traps that make cron jobs run at the wrong time: the day-of-month/day-of-week rule and time zones.
How to Use the Generator
1. Work left to right through the five fields. Each field has its own dropdown: choose “Every” for an asterisk, “Every N” for a step value, “Specific” for a comma-separated list, or “Range” for a from–to span, then fill in the numbers the mode asks for. 2. Generate and read it back. The plain-language reading is the check that matters: if it does not say what you meant, the expression is wrong, however plausible it looks. 3. Check the next 5 run times. The preview lists the upcoming runs so you can spot a schedule that fires far more (or far less) often than you intended before it ever reaches your server. 4. Copy it into your scheduler. The generated format is the standard, portable 5-field form used by Linux crontab and most schedulers — no seconds field, no year field, and no shortcut strings. When the schedule drives a job that produces content on a deadline, our regex guide helps validate the outputs it produces, and our JSON guide covers the payloads scheduled jobs most often emit.
The Five Fields, in Order
The fields are, left to right: minute (0–59), hour (0–23, on the 24-hour clock), day of month (1–31), month (1–12, where 1 is January), and day of week (0–6, where 0 is Sunday). An asterisk in a field means “every possible value” for that field. So 0 9 * * 1-5 reads as: at minute 0, hour 9, any day of the month, any month, days of the week 1 through 5 — that is, at 9:00 AM, Monday through Friday. And 30 14 1 * * reads as: at 2:30 PM on the 1st of every month. The most common beginner error is reading the fields in the wrong order or in 12-hour time: hour 14 is 2 PM, not 2 AM, and there is no AM/PM marker anywhere in a cron expression. Say each expression out loud in field order until the reading feels automatic; the plain-language preview in the generator is there to confirm your reading, not to replace it.
Four Ways to Fill a Field: Every, Step, List, Range
Every field accepts four shapes, and the generator's dropdowns map to them directly. “Every” is the asterisk: * in the minute field means every minute. “Every N” is the step syntax */n: */5 in the minute field means every 5 minutes, at :00, :05, :10 and so on, always stepping from the smallest valid value of the field — so */15 gives :00, :15, :30, :45, and */6 in the hour field gives midnight, 6 AM, noon and 6 PM. “Specific” is a comma-separated list: 1,15 in the day-of-month field means the 1st and the 15th only. “Range” is a from–to span with a hyphen: 1-5 in the day-of-week field means Monday through Friday, and 9-17 in the hour field means 9 AM through 5 PM. These combine freely across fields: */15 9-17 * * 1-5 is every 15 minutes during business hours on weekdays — a schedule that would take a paragraph to describe in words and takes one short string in cron. Note what is not here: ranges and steps do not need a seconds field, and a 6-field expression with seconds belongs to a different scheduler dialect, not to portable cron.
Worked Examples: Five Schedules You Will Actually Use
Every night at 2 AM, for backups and cleanup: 0 2 * * * — minute 0, hour 2, everything else unrestricted. Every weekday morning at 9, for a report or a digest: 0 9 * * 1-5. Every 15 minutes around the clock, for a health check: */15 * * * *. On the 1st of every month at 2:30 PM, for a monthly invoice run: 30 14 1 * *. Twice a day, at 8 AM and 8 PM, for a sync job: 0 8,20 * * * — the list form in the hour field. Build each one in the generator and compare the plain-language reading and the next 5 run times against what you intended; the 2 AM backup in particular deserves a second look at the time-zone section below, because 2 AM on your laptop and 2 AM on a cloud server are often different moments. A schedule that looks right in the preview and wrong on the server is almost always a time-zone schedule, not a cron-expression error.
The Two Traps: The Day OR Rule, and Time Zones
Trap one: when both day of month and day of week are restricted, cron uses OR, not AND. This is the single most surprising rule in cron, and it bites everyone once. 0 9 15 * 1 does not mean “9 AM on the 15th when it is a Monday.” It means “9 AM on the 15th of every month AND every Monday” — the job fires when either condition matches. If you genuinely want only the days that satisfy both conditions, cron cannot express it; put the extra condition inside your script instead, and let cron fire on the wider schedule. Trap two: cron has no time-zone field. Jobs run at the scheduled time in the server's local time zone, which on cloud servers is often UTC. The generator's next-5-runs preview uses your browser's local time zone, so compare it against your scheduler's time zone before deploying: a 9 AM job on a UTC server is 4 AM Eastern during daylight saving time, and a “morning” report that arrives at 4 AM is a time-zone bug, not a cron bug. Daylight saving itself adds a wrinkle — a 2:30 AM job may not run, or may run twice, on the two clock-change nights — so avoid scheduling critical jobs in the 2 AM hour if your server observes daylight saving.
Common Mistakes
Adding a seconds field. Standard cron has five fields; a six-field string with seconds belongs to another dialect and will be rejected or misread by a five-field scheduler. Using @daily, L, W or # and expecting portability. Those shortcuts and special characters exist in some implementations and not in others; the generator deliberately produces only the portable five-field form, so a generated expression works wherever standard cron works. Restricting both day fields and expecting AND. See the OR rule above — it is the most expensive single misunderstanding in cron. Forgetting the 24-hour clock. Hour 2 is 2 AM; 2 PM is hour 14. Testing in production. A schedule that fires every minute because of a misplaced asterisk is a self-inflicted denial of service; the next-5-runs preview exists precisely so the first run happens on screen, not on your server. Ignoring the server's time zone. Check it once, write it down next to the expression, and re-check it after any server migration.
Limits Worth Knowing
Portable cron cannot express “the last Friday of the month,” “every other Tuesday,” or “the first weekday after the 15th” directly — those need a wider cron schedule plus a condition inside the script, or a scheduler with richer expressions. Intervals finer than one minute are also out of reach; sub-minute repetition belongs in the job itself, not in the schedule. Within those limits, five fields cover nearly every recurring job in practice, which is why a format from the 1970s still runs the modern internet's schedules.
Frequently Asked Questions
What is a cron expression?
What do the five fields in a cron expression mean?
What does */5 mean in a cron expression?
Why does my cron job run more often than expected when I set both day of month and day of week?
What time zone does a cron expression use?
More guides from ToolNest
How to Test Regular Expressions: Patterns, Flags and Capture Groups Explained
Pattern box, Email/URL/US Phone presets, the six flags, and capture-group details — test the pattern before it ever reaches your code.
How to Encode and Decode Base64: What It Is, What It Is Not, and When to Use It
Three bytes become four characters, output grows by about a third, and the = padding marks where the data ends — plus the one rule that matters: Base64 is not encryption.
How to Format JSON: Validation, Indentation and the Errors That Break Parsers
Paste, validate, pretty-print: the formatter that pinpoints the exact error, counts keys and depth, and turns a one-line blob into readable, diffable JSON.