Why Your Cron Schedule Is Not Working
You set the schedule, saved it, and nothing happened. Or it ran at 3 a.m. instead of 3 p.m. Cron validation is the step most people skip, and it is the step that catches almost every broken schedule. This guide shows you how to read a cron expression field by field, how to validate it before it reaches production, and how to debug the failures that only show up at runtime.
A cron expression is a string of five or six fields that tells a scheduler when to run a job. Each field controls one unit of time: minute, hour, day of month, month, and day of week. Get one field wrong and the job either never fires or fires far more often than you intended.
What Cron Validation Actually Checks
Validation is not one check. It is several, and they catch different classes of mistake.
- Field count. Five fields for standard schedules, six when a seconds field is present. A four-field expression is almost always a typo.
- Range. Minutes run 0-59, hours 0-23, days of month 1-31, months 1-12, days of week 0-6. A value of
75in the minute field is invalid, not a shorthand. - Step syntax.
*/15means "every 15 units".15/15means something different, and the difference matters. - List and range syntax.
1,15,30is a list.9-17is a range. Mixing them without care produces schedules that look right and are not. - Name aliases. Month and weekday names are often accepted, but support varies between schedulers.
A validator flags all five. Reading the expression yourself catches maybe two.
The difference between syntax and semantics
Syntax validation asks whether the expression is well-formed. Semantic validation asks whether it means what you think it means.
0 0 31 2 * is syntactically perfect. It is also a schedule that runs on February 31st, which does not exist. Most validators accept it. The job simply never runs.
That gap is where most production incidents live.
How to Validate a Cron Expression Step by Step
- Count the fields. Write the expression out and count. Five or six, nothing else.
- Label each field. Minute, hour, day of month, month, day of week. Do not skip this. Most bugs are a field in the wrong position.
- Check every range. Confirm each value sits inside its field's allowed range.
- Resolve the special characters. Expand
,,,-, and/into plain language./5becomes "every 5". - Read it back as a sentence. "At minute 0, hour 9, every day of month, every month, on Monday through Friday."
- Confirm against a known time. Ask what the next three run times are. If you cannot answer, the expression is not ready.
- Test in a sandbox first. Run the job against a staging environment before it touches production data.
A browser-based validator does steps 1 through 5 in under a second, which is why it is worth doing before you commit anything.
A worked example
Take 30 2 1.
- Minute 30
- Hour 2
- Every day of month
- Every month
- Day of week 1, which is Monday
Read back: "At 02:30, every Monday." That is a weekly job, not a daily one. The 1 in the last field is doing all the work, and it is the field people forget to check.
Why Scheduling Expressions Keep Failing in Production
Syntax errors are the easy failures. They show up immediately. The hard ones pass validation and still misbehave.
Timezone assumptions
A server running in UTC will fire a job at a different local time than a developer working in another zone expects. 0 9 * means 9 a.m. UTC, not 9 a.m. wherever you are sitting. This is the single most common cause of "it ran at the wrong time" reports, and no syntax validator will catch it. You need to know your scheduler's timezone, and you need to set it explicitly rather than relying on a default.
Day-of-month and day-of-week interaction
When both the day-of-month and day-of-week fields are restricted, most cron implementations treat them as OR, not AND. So 0 0 13 * 5 fires on the 13th of the month and on every Friday. Most people reading that expression assume it means "the Friday that falls on the 13th". It does not.
If you want that behaviour, you have to build it in the job itself, not the schedule.
Daylight saving transitions
In zones that observe daylight saving, the hour between 2 and 3 a.m. can occur twice in autumn and not at all in spring. A job scheduled for 2:30 a.m. may run twice or not at all on those days. Schedulers handle this differently, and some do not handle it at all. If your job must run exactly once per day, schedule it well outside the transition window.
Overlapping runs
If a job takes longer than its interval, the next run starts before the previous one finishes. /5 * on a job that takes six minutes will pile up. This is not a cron bug, but it presents as one: duplicated work, race conditions, and inconsistent data.
The schedule is not the only input. Runtime duration, timezone, and scheduler behaviour all shape when a job actually runs.
Debugging a Schedule That Passes Validation
When the expression is correct and the job still fails, move down the stack.
Check the scheduler's own logs first. Most will tell you whether the job was triggered and whether it exited cleanly. A job that triggered and failed is a different problem from one that never triggered.
Confirm the job is registered. A schedule with no registered task is silently ignored by some systems.
Look at the last successful run. If it stopped at a specific point, something changed at that point.
Test the command in isolation. Run the task manually with the same environment the scheduler uses. Environment variables, working directories, and user permissions differ between your shell and the scheduler's.
Narrow the interval temporarily. Change the schedule to run every minute, confirm it fires, then restore the real interval. This separates "the schedule is wrong" from "the schedule is right and the job is broken".
That last step resolves most cases in a few minutes.
What a Cron Expression Validator Cannot Do
An online validator reads the expression. It has no visibility into your server, your timezone configuration, your scheduler version, or your job's runtime.
It will not tell you:
- Whether the job will overlap with itself
- How your specific scheduler handles daylight saving
- Whether the underlying command will succeed
- Whether the timezone on the host is what you assume
Use it to eliminate syntax errors and misread fields. That is a large share of real failures, and it is the share you can fix in seconds. The rest requires looking at the runtime.
You can run the expression check in your browser through the cron expression validator without sending your schedule anywhere, and browse the rest of the available tools if you need related utilities.
Frequently Asked Questions
How many fields should a cron expression have?
Five fields for standard schedules: minute, hour, day of month, month, day of week. Six fields when a leading seconds field is present. If you have four or seven, the expression is malformed. Check the field count before anything else, because a missing or extra field shifts every value into the wrong position.
Does 0 0 * run at midnight?
Yes, in the timezone the scheduler is configured for. That is the catch. On a server set to UTC, it runs at midnight UTC, which is a different local time elsewhere. Confirm the scheduler's timezone before assuming the job runs when you expect.
Why does my job run on the wrong day of the week?
Day-of-week numbering varies. Some systems treat Sunday as 0, others as 7, and some accept both. If your job runs a day early or late, check the numbering convention your scheduler uses rather than adjusting the number until it happens to work.
What does */15 mean in the minute field?
It means every 15 minutes: minutes 0, 15, 30, and 45. The asterisk stands for the full range, and the step value divides it. */15 in the hour field would mean every 15 hours, which is rarely useful. The meaning depends entirely on which field it sits in.
Can a cron expression run on the last day of the month?
Not reliably with 31, since not every month has 31 days, and February never does. Most schedulers do not support "last day" natively. The usual approach is to run the job daily and have the job itself check whether today is the final day of the month.
Getting the Schedule Right the First Time
Most broken schedules are broken before they reach the server. A field in the wrong position, a range that does not exist, a day-of-week number read with the wrong convention. Cron validation catches that entire class of error in seconds, and it costs nothing to run before you commit.
The failures that survive validation are the ones that need runtime investigation: timezone configuration, overlapping runs, and scheduler-specific behaviour around daylight saving. Knowing which category you are in tells you where to look next.
Validate the expression first. Then debug the job.