DEV Community

Cover image for Cron expressions explained — a complete guide with real examples
William Andrews
William Andrews

Posted on • Originally published at devcrate.net

Cron expressions explained — a complete guide with real examples

Cron is one of those tools that every developer encounters eventually. You need to run a database backup every night, send a weekly digest email, or poll an API every five minutes — and someone mentions cron. You look up the syntax, copy something from Stack Overflow, it works, and you move on. Then six months later you need to change the schedule and you're staring at five numbers wondering what each one means again.

This guide is meant to be the one you save. After reading it you should be able to read and write any cron expression from scratch, without guessing.


What cron actually is

Cron is a time-based job scheduler built into Unix-like operating systems. A cron job is a command or script that cron runs automatically on a defined schedule. The schedule is defined by a cron expression — a string of five fields that describe when to run the job.

Cron expressions show up everywhere: Linux crontabs, GitHub Actions schedules, AWS EventBridge, Kubernetes CronJobs, Vercel cron functions, Railway cron jobs, and more. The syntax is largely the same across all of them, with minor variations.


Anatomy of a cron expression

A standard cron expression has five fields separated by spaces:

┌─────────── minute (059)
│ ┌───────── hour (023)
│ │ ┌─────── day of month (131)
│ │ │ ┌───── month (112)
│ │ │ │ ┌─── day of week (07, 0 and 7 are both Sunday)
│ │ │ │ │
* * * * *
Enter fullscreen mode Exit fullscreen mode

A helpful mnemonic: M H D M W — Minutes, Hours, Days, Months, Weekdays.


Field ranges and allowed values

Field Range Special characters
Minute 0–59 * , - /
Hour 0–23 * , - /
Day of month 1–31 * , - / ?
Month 1–12 or JAN–DEC * , - /
Day of week 0–7 or SUN–SAT (0 and 7 are both Sunday) * , - / ?

Special characters

* — every

An asterisk means "every valid value for this field." * in the minute field means every minute. * in the hour field means every hour.

, — list

A comma lets you specify multiple values. 1,15,30 in the minute field means at minute 1, 15, and 30.

- — range

A hyphen defines a range. 9-17 in the hour field means every hour from 9am to 5pm inclusive.

/ — step

A slash defines a step interval. */5 in the minute field means every 5 minutes. 0-30/10 means every 10 minutes between minute 0 and minute 30.

? — no specific value

Used in day-of-month and day-of-week fields to mean "I don't care." When you specify a day of week, use ? in the day-of-month field, and vice versa. Not all cron implementations support this — standard Linux crontab uses * instead.


Real examples

# Run at midnight every day
0 0 * * *
Enter fullscreen mode Exit fullscreen mode
# Run every 5 minutes
*/5 * * * *
Enter fullscreen mode Exit fullscreen mode
# Run at 9am Monday through Friday
0 9 * * 1-5
Enter fullscreen mode Exit fullscreen mode
# Run at 6am and 6pm every day
0 6,18 * * *
Enter fullscreen mode Exit fullscreen mode
# Run at 2:30am on the 1st of every month
30 2 1 * *
Enter fullscreen mode Exit fullscreen mode
# Run every weekday at noon
0 12 * * 1-5
Enter fullscreen mode Exit fullscreen mode
# Run at 11:59pm on December 31st
59 23 31 12 *
Enter fullscreen mode Exit fullscreen mode
# Run every 15 minutes between 8am and 5pm on weekdays
*/15 8-17 * * 1-5
Enter fullscreen mode Exit fullscreen mode

Common mistakes

Confusing day-of-week numbering

Different systems handle Sunday differently. In standard crontab, Sunday is both 0 and 7. In some systems, 0 is Sunday and 6 is Saturday. In others, 1 is Monday and 7 is Sunday. Always check the documentation for the platform you're using. When in doubt, use the three-letter abbreviation (SUN, MON, etc.) if the platform supports it — it's unambiguous.

Forgetting timezone

Cron runs in the timezone of the server unless configured otherwise. If your server is UTC and your users are in EST, a job scheduled for 0 9 * * * runs at 4am local time for East Coast users. Always be explicit about timezone. Most modern platforms (GitHub Actions, AWS, Vercel) let you specify timezone separately.

Thinking */5 means "every 5 minutes starting now"

*/5 in the minute field means at minutes 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, and 55 — fixed to the clock, not relative to when the job was added. If you add a job at 2:03pm with */5, it first runs at 2:05pm, not 2:08pm.

Using both day-of-month and day-of-week

If you specify a value in both the day-of-month and day-of-week fields (rather than using * in one), most cron implementations treat them as an OR condition, not AND. 0 0 1 * 1 runs at midnight on the 1st of every month and every Monday — not just on Mondays that fall on the 1st. If you want "the first Monday of the month" you need a workaround, since standard cron can't express that directly.


Platform-specific variations

Standard Linux crontab uses the five-field format above. But many platforms extend or modify it:

  • GitHub Actions uses standard five-field cron, always in UTC. The minimum interval is every 5 minutes.
  • AWS EventBridge (CloudWatch Events) uses a six-field format with an optional seconds field, and uses ? for the day-of-month/day-of-week conflict. It also adds L (last) and W (weekday nearest to) special characters.
  • Kubernetes CronJobs use standard five-field cron and support @hourly, @daily, @weekly, @monthly, and @yearly shortcuts.
  • Vercel and Railway use standard five-field cron. Railway requires a minimum interval of 1 minute.

Shorthand expressions

Many cron implementations support named shortcuts for common schedules:

Shorthand Equivalent Meaning
@yearly 0 0 1 1 * Once a year at midnight on January 1st
@monthly 0 0 1 * * Once a month at midnight on the 1st
@weekly 0 0 * * 0 Once a week at midnight on Sunday
@daily 0 0 * * * Once a day at midnight
@hourly 0 * * * * Once an hour at the start of the hour
@reboot Once at startup (Linux crontab only)

How to read an unfamiliar expression

When you encounter a cron expression you don't immediately recognize, read it field by field left to right: minute, hour, day-of-month, month, day-of-week. Ask yourself: is this field a specific value, a range, a list, a step, or a wildcard? Build up the meaning piece by piece.

Take 0 */6 * * *. Minute is 0 — on the hour. Hour is */6 — every 6 hours (0, 6, 12, 18). Day, month, and day-of-week are all wildcards. So: at midnight, 6am, noon, and 6pm, every day.

Take 30 8 * * 1. Minute 30, hour 8, day-of-month wildcard, month wildcard, day-of-week 1 (Monday). So: 8:30am every Monday.


Try it without deploying anything

I built the DevCrate cron builder because I found myself testing expressions by deploying jobs and watching logs — which is a slow, painful way to verify a schedule.

The tool lets you paste any expression and instantly see the next several run times in plain English, without deploying anything. It also works the other way: pick a schedule from the visual builder and it generates the expression for you. Free, runs entirely in your browser, nothing to sign up for.

Free Online Cron Expression Builder — DevCrate

Build and validate cron expressions with a visual editor. See human-readable output and next 10 run times instantly.

favicon devcrate.net

Top comments (0)