Production Engineering/
Lesson

Every production system runs tasks on a schedule. Database backups at 3 AM, cache cleanup every hour, weekly analytics reports, health checks every 5 minutes. The language that describes all of these schedules is cron — a format invented in the 1970s that's still the universal standard today. When you see 0 */6 * * * in a GitHub Actions workflow or a Cloudflare Worker trigger, that's cron.

You don't need to memorize every combination. You need to read cron expressions when you encounter them and write basic ones when AI gets them wrong — because AI gets them wrong surprisingly often.

The 5-field format

Every cron expression has exactly 5 fields, separated by spaces, always in the same order:

┌───────── minute (0-59)
│ ┌─────── hour (0-23)
│ │ ┌───── day of month (1-31)
│ │ │ ┌─── month (1-12)
│ │ │ │ ┌─ day of week (0-6, Sunday = 0)
│ │ │ │ │
* * * * *

Read it left to right: minute, hour, day, month, weekday. That's the one thing to burn into memory. Everything else follows from understanding what each field accepts.

AI pitfall
AI frequently generates cron expressions with the hour and minute swapped. If you ask for "every day at 9:30 AM" and get 9 30 * * *, that's wrong — it means "at minute 9 of hour 30" which doesn't exist. The correct expression is 30 9 * * *.
02

Field operators

Each field accepts more than just single numbers. Here are the four operators you'll see:

OperatorMeaningExampleTranslates to
*Every possible value* * * * *Every minute of every hour
,List of values0,30 * * * *At minute 0 and minute 30
-Range0 9-17 * * *Every hour from 9 AM to 5 PM, at minute 0
*/nEvery nth value*/15 * * * *Every 15 minutes (0, 15, 30, 45)

You can combine operators in a single field. 1-5 in the weekday field means Monday through Friday. 0,30 in the minute field means twice per hour at :00 and :30.

Reading examples

Let's decode a few real-world expressions:

0 * * * *        → At minute 0 of every hour (hourly, on the hour)
30 2 * * *       → At 2:30 AM every day
0 9 * * 1-5      → At 9:00 AM, Monday through Friday
*/10 * * * *     → Every 10 minutes
0 0 1 * *        → At midnight on the 1st of every month
0 6 * * 0        → At 6:00 AM every Sunday
15 14 1 * *      → At 2:15 PM on the 1st of every month

The trick to reading cron: start from the right. The rightmost non-* field tells you the biggest cycle. Then work left for the exact timing.

Good to know
Some systems support a 6th field for seconds, or accept 7 for Sunday alongside 0. GitHub Actions uses the standard 5-field format but runs in UTC. Always check your platform's docs for subtle differences.
03

Common patterns you'll encounter

These are the cron expressions you'll see most often in codebases. Bookmark this table — it covers 90% of real-world use cases.

ScheduleExpressionHow to remember
Every minute* * * * *All stars = all the time
Every 5 minutes*/5 * * * *Step on minute field
Every hour0 * * * *Minute 0, every hour
Every day at midnight0 0 * * *Minute 0, hour 0
Every day at 9 AM0 9 * * *Minute 0, hour 9
Weekdays at 8:30 AM30 8 * * 1-5Mon-Fri range on weekday
Every Monday at 6 AM0 6 * * 1Weekday 1 = Monday
1st of month at midnight0 0 1 * *Day 1
Every 6 hours0 */6 * * *Step on hour field
Twice a day (9 AM, 9 PM)0 9,21 * * *Comma list on hour
04

Where cron is used in practice

You'll run into cron syntax in these contexts:

GitHub Actions

The schedule trigger uses cron directly:

yaml
on:
  schedule:
    - cron: '0 2 * * *'  # Every day at 2 AM UTC

Cloudflare Workers (Cron Triggers)

toml
[triggers]
crons = ["*/5 * * * *"]  # Every 5 minutes

Linux crontab

The original. Edit with crontab -e:

# Backup database every night at 3 AM
0 3 * * * /usr/local/bin/backup.sh

# Clean temp files every Sunday at midnight
0 0 * * 0 /usr/local/bin/cleanup.sh

Node.js (node-cron)

import cron from 'node-cron';

// Send weekly report every Monday at 9 AM
cron.schedule('0 9 * * 1', () => {
  sendWeeklyReport();
});
AI pitfall
When you ask AI to set up a scheduled job, it sometimes uses human-readable libraries like every('5 minutes') instead of cron syntax. These libraries add a dependency you probably don't need — most platforms already understand cron natively.
05

Debugging cron schedules

When a scheduled job doesn't fire when expected, check these three things:

1. Timezone

Most cron systems run in UTC, not your local time. If your backup runs at "the wrong hour", it's probably correct in UTC.

# You want 9 AM Paris time (UTC+2 in summer)
# In UTC, that's 7 AM
0 7 * * *

2. Field order mistakes

The number one cron bug: swapping minute and hour. Read your expression back out loud.

# WRONG: "At hour 30, minute 9" — hour 30 doesn't exist
9 30 * * *

# RIGHT: "At minute 30, hour 9"9:30 AM
30 9 * * *

3. Day-of-month vs day-of-week conflicts

If you set both day-of-month AND day-of-week, most implementations run the job when either matches (OR logic), not when both match (AND logic). This is a classic source of "why does my job run on unexpected days?"

# Intended: "1st of the month, only if it's a Monday"
# Actual: "every 1st of the month AND every Monday"
0 0 1 * 1

# To get "1st Monday of the month", you need scripting logic,
# cron alone can't express this
06

Quick reference

FieldPositionRangeSpecial values
Minute1st0-59*/5 = every 5 min
Hour2nd0-239-17 = business hours
Day of month3rd1-311,15 = 1st and 15th
Month4th1-121-3 = Jan to Mar
Day of week5th0-61-5 = Mon to Fri
SymbolMeaningExample
*Every* * * * * = every minute
,List0,30 = at 0 and 30
-Range9-17 = 9 through 17
*/nStep*/10 = every 10th value