A cron expression is a string of five fields that tells a scheduler when to run a recurring job. The fields are minute, hour, day of month, month, and day of week, in that order. The expression 0 9 * * 1 means run at 9:00 AM every Monday. The expression 30 2 * * * means run at 2:30 AM every day. Once you know the five fields and four special characters, you can read any cron expression.
A backend engineer in Berlin setting up a nightly database backup needs it to run at 03:15 server time, every weekday. The cron expression is 15 3 * * 1-5. A marketing automation setup in Austin needs a weekly digest sent at 10:00 AM on Fridays, which is 0 10 * * 5. Both are five fields, read left to right.
The five fields
Standard cron uses five fields. Some systems add a sixth field for seconds at the front, and a few add a seventh for year at the end, but the classic Unix cron is five.
- Field 1, minute: 0 to 59
- Field 2, hour: 0 to 23 (24-hour clock)
- Field 3, day of month: 1 to 31
- Field 4, month: 1 to 12, or names like JAN to DEC
- Field 5, day of week: 0 to 7, where 0 and 7 are both Sunday, or names like MON to SUN
The special characters
An asterisk means every value of that field. A star in the minute field means every minute, and a star in the day-of-week field means every day of the week. A comma separates a list of values, so 1,15 in the day-of-month field means the 1st and the 15th. A hyphen defines a range, so 9-17 in the hour field means every hour from 9 AM to 5 PM. A slash defines a step, so */15 in the minute field means every 15 minutes, and 0-23/2 means every 2 hours.
- * matches every value in the field
- , matches a list, like 1,15,30
- - matches a range, like 9-17
- / sets a step, like */15 for every 15 minutes
- L means the last, like L in day-of-week for the last Saturday
- W means the nearest weekday, like 15W for the weekday closest to the 15th
The L, W, and # characters are extensions from Quartz and Spring Scheduler, not classic Unix cron. They work in most modern schedulers, including Kubernetes CronJobs and AWS EventBridge, but they fail in plain Vixie cron on Linux. Check your scheduler's docs before relying on them.
Worked examples
Example 1, every weekday at 9:00 AM. The expression is 0 9 * * 1-5. Minute 0, hour 9, every day of month, every month, Monday through Friday. This is the most common cron expression in business automation.
Example 2, every 15 minutes. The expression is */15 * * * *. The step value in the minute field fires at 0, 15, 30, and 45 past every hour. A monitoring script in Dublin uses this to ping a health endpoint 96 times a day.
Example 3, first day of every month at midnight. The expression is 0 0 1 * *. Minute 0, hour 0, day of month 1, every month, every day of week. A billing run in Singapore uses this to generate invoices on the 1st.
Example 4, every Sunday at 02:00. The expression is 0 2 * * 0. The 0 in the day-of-week field is Sunday. Some people prefer 7 for Sunday, which also works, but 0 is the more common convention.
Example 5, the last Friday of every month at 17:00. In a scheduler that supports L, the expression is 0 17 * * 5L. The 5 is Friday and L means the last occurrence of that weekday in the month. In plain Unix cron, you cannot express this directly and need a wrapper script that checks the date.
The day-of-month and day-of-week interaction
This is the part that trips people up. When both the day-of-month and day-of-week fields are restricted (not a star), cron runs the job when either field matches, not both. The expression 0 0 13 * 5 means midnight on the 13th of every month OR midnight on any Friday, not midnight on Friday the 13th only. If you want Friday the 13th, you need a script that checks both conditions and exits early otherwise.
If either the day-of-month or day-of-week field is a star, the other field alone controls the schedule. The OR behavior only applies when both are restricted. This is the single most misunderstood cron rule.
Time zones and DST
Classic Unix cron runs in the server's local time, which means a job scheduled at 02:30 runs twice or not at all on DST transition days. On fall-back, 02:30 occurs twice, so the job runs twice. On spring-forward, 02:30 does not occur, so the job skips. For jobs that must run exactly once, schedule them outside the 02:00 to 03:00 window, or run the scheduler in UTC. Kubernetes CronJobs and systemd timers support a timezone setting, and using UTC avoids the DST edge entirely.
Related tools for scheduling
The Cron Generator builds an expression from a plain-language schedule and explains what it will fire. The Unix Timestamp converter helps when a job needs to log instants in UTC. The Timer and Stopwatch tools handle one-off countdowns rather than recurring schedules. The Discord Timestamp generator formats instants for chat logs.