Cron Expressions Explained: How to Read a Schedule
Photo by Unsplash on Unsplash
Table of Contents
The Short Version
Cron has been the backbone of scheduled tasks on Unix systems for decades, and the same syntax now shows up far beyond the terminal — in CI pipelines, serverless platforms, container orchestrators, and countless app frameworks. Learning to read cron once means you can schedule things almost anywhere. It's one of those small, durable skills that keeps paying off, so it's well worth the ten minutes it takes to actually understand.
The Five Fields
The ranges are worth memorizing: minute is 0-59, hour is 0-23 (24-hour clock), day of month is 1-31, month is 1-12, and day of week is 0-6 where 0 is Sunday. Many systems also accept names like `MON` or `JAN`, which are easier to read. Once you internalize the order and ranges, reading a cron string becomes mechanical: you just walk across the five slots and translate each one. The hard part isn't the fields themselves — it's the special characters, which is where the real power and the real confusion live.
The Special Characters
Those four combine to express almost any schedule. Want every weekday at 9 AM? That's `0 9 * * 1-5`. Every 15 minutes? `*/15 * * * *`. The first of every month at midnight? `0 0 1 * *`. Reading them is just a matter of applying these rules field by field. I still occasionally mix up which slash-step lands where, so I always double-check a fresh expression before trusting it in production — more on that below.
Reading Real Examples
Notice the method: I never try to read the whole thing at once. I go field by field — minute, then hour, then the date fields — and assemble the meaning piece by piece. That discipline is what turns cron from cryptic to obvious. If you're the type who also likes to understand the systems cron jobs often touch, my explainer on what a CDN is and other infra pieces pair well, since scheduled jobs frequently drive cache purges and background maintenance.
The Day-of-Month vs Day-of-Week Trap
If you only ever constrain one of the two day fields and leave the other as `*`, you'll never hit this landmine. The trouble comes exclusively when both are set. I've seen production jobs fire far more often than expected because someone assumed AND logic. So my rule of thumb is simple: constrain day-of-month or day-of-week, but not both, unless you genuinely understand your scheduler's OR behavior and want it.
Seconds, Timezones, and Non-Standard Cron
Timezones are the other quiet trap. A cron job runs in whatever timezone the scheduler is configured for, which is often UTC on servers. `0 9 * * *` means 9 AM — but 9 AM where? If you assume your local time and the server runs UTC, your job fires at the wrong hour. Always confirm the scheduler's timezone, and when in doubt, reason in UTC. If dates and times in general trip you up, precise timestamp handling is its own skill worth sharpening. If you schedule jobs that stamp or compare times, a reliable Unix timestamp converter is worth keeping in a browser tab, because half of all cron confusion is really timezone confusion in disguise.
Double-Check Before You Deploy
There are excellent free tools that take a cron expression and show you the next several run times in human-readable form, which is the fastest sanity check available. I paste every non-trivial expression into one before shipping it. The official crontab manual and community references like the widely used crontab guru are great for confirming edge cases and symbol behavior. Two minutes of verification beats discovering at 3 AM that your 'nightly' backup has been running every minute.
Writing Your Own From Scratch
Start simple and add constraints only as needed — the more `*` wildcards you leave in, the easier the expression is to read and the less likely you are to hit the day-field trap. And always, always verify the result before deploying. Cron is a wonderfully durable tool, but it does exactly what you tell it, including when what you told it isn't what you meant. Read it back, confirm the next run times, and then ship with confidence.
Frequently Asked Questions
How do you read a cron expression?
Read the five fields left to right: minute, hour, day of month, month, and day of week. Translate each field one at a time using its number range, then combine them. For example, `30 2 * * 1` means 2:30 AM every Monday. Going field by field is the reliable method.
What do the symbols in cron mean?
An asterisk (*) means 'every,' a comma (,) lists specific values, a hyphen (-) gives a range, and a slash (/) defines steps. So `*/15` means every 15 minutes, `1-5` means Monday to Friday in the weekday field, and `0,30` means at minute 0 and 30.
Why does my cron job run more often than expected?
The most common cause is setting both the day-of-month and day-of-week fields, which most cron implementations treat as OR, not AND. So `0 0 13 * 5` runs on the 13th OR any Friday, not only Friday the 13th. Constrain just one of the two day fields to avoid this.
How many fields does a cron expression have?
Standard Unix cron has five fields: minute, hour, day of month, month, and day of week. Some modern schedulers add a sixth field for seconds at the front, and Quartz-style cron uses six or seven fields. Always check how many fields your specific scheduler expects.
What timezone do cron jobs run in?
Cron jobs run in whatever timezone the scheduler is configured for, which is frequently UTC on servers. If you assume local time but the server uses UTC, your job fires at the wrong hour. Always confirm the scheduler's timezone, and reason in UTC when unsure. See my related infra guide on [what a CDN is](/blog/what-is-a-cdn-how-content-delivery-networks-work).
Try ToolsFuel
23+ free online tools for developers, designers, and everyone. No signup required.
Browse All Tools