Understanding Cron Jobs: How to Read and Write Schedules

Published 2026-05-10

Behind almost every recurring automated task — nightly backups, hourly data syncs, the weekly report that lands in your inbox every Monday — there is usually a cron job. Cron is the time-based scheduler that has run the Unix world since the 1970s, and its compact syntax now appears far beyond Unix: in GitHub Actions, Kubernetes, cloud functions, and CI pipelines. Learning to read it is a genuinely useful skill.

The five fields

A standard cron expression is five fields separated by spaces. Each field controls one unit of time:

*  *  *  *  *
|  |  |  |  |
|  |  |  |  +-- day of week  (0-7, 0 and 7 = Sunday)
|  |  |  +----- month        (1-12)
|  |  +-------- day of month (1-31)
|  +----------- hour         (0-23)
+-------------- minute       (0-59)

An asterisk (*) means "every value." So * * * * * means "every minute of every hour of every day" — the most frequent schedule cron allows.

The special characters

Each field accepts more than just a number or an asterisk:

  • Comma for lists: 0,15,30,45 in the minute field means at 0, 15, 30, and 45 minutes past the hour.
  • Hyphen for ranges: 1-5 in the day-of-week field means Monday through Friday.
  • Slash for steps: */15 in the minute field means every 15 minutes; */2 in the hour field means every other hour.

Reading real examples

0 9 * * 1-5     → at 09:00, Monday to Friday
*/15 * * * *    → every 15 minutes
0 0 1 * *       → at midnight on the 1st of every month
30 3 * * 0      → at 03:30 every Sunday
0 */6 * * *     → every 6 hours (00:00, 06:00, 12:00, 18:00)

The pattern to internalize: read the fields left to right as minute, hour, day-of-month, month, day-of-week, and translate each special character. With a little practice, 0 9 * * 1-5 reads instantly as "9 AM on weekdays."

The timezone gotcha that causes outages

Here is the mistake that has broken countless production systems: classic cron has no timezone field. A job runs in whatever timezone the host machine is set to. Most servers run in UTC, so a job you scheduled for "9 AM" fires at 9 AM UTC — which might be 10 or 11 AM where you live, and shifts by an hour when daylight saving time changes.

If a scheduled report arrives an hour "late" twice a year, this is almost always why. When precise local timing matters, either set the schedule in UTC deliberately, or use a scheduler that supports an explicit timezone.

The day-of-month and day-of-week trap

One more surprise: when you set both the day-of-month and the day-of-week fields, standard cron combines them with OR, not AND. So 0 0 13 * 5 does not mean "midnight on Friday the 13th" — it means "midnight on the 13th of every month, and midnight every Friday." If you only want one of the two conditions, leave the other as *.

Test before you deploy

Because a single wrong character can mean a backup that silently never runs, always verify a cron expression before trusting it. The parser below translates any expression into plain English and shows the next several run times, so you can confirm a schedule does exactly what you intended.

Related tool: Cron Expression Parser — Parse cron expressions and see the next scheduled run times.
Copied!