DevPlainly
Developer Productivity

Cron Expressions Explained: Syntax, Examples and Common Mistakes

Learn to read and write cron expressions with clear examples, a field-by-field breakdown, and the mistakes that make scheduled jobs silently fail.

· 7 min read

Contents
  1. The five fields
  2. Special characters
  3. 15 cron expressions you will actually use
  4. The day-of-month vs day-of-week trap
  5. Time zones
  6. Editing your crontab on Linux
  7. Common mistakes (and how to fix them)
  8. 1. The job works in the terminal but not in cron
  9. 2. Unescaped percent signs
  10. 3. No output, no idea what happened
  11. 4. Confusing /N with "every N from now"
  12. 5. Overlapping runs
  13. Cron in other tools
  14. FAQ
  15. What does mean?
  16. How do I run a cron job every 30 seconds?
  17. Is Sunday 0 or 7?
  18. How can I test a cron expression without waiting?
  19. Further reading

Cron is the scheduler that has been running jobs on Unix systems since the 1970s, and its syntax shows up everywhere today: Linux crontabs, GitHub Actions, Kubernetes CronJobs, cloud schedulers and most CI tools. Once you can read the five fields, you can read all of them.

This guide walks through the syntax, gives you copy-paste examples for the schedules people actually need, and covers the mistakes that cause jobs to run at the wrong time — or never. If you just want to check an expression, paste it into our free Cron Expression Explainer.

The five fields

A standard cron expression has five fields separated by spaces:

┌───────────── 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 Sunday)
│ │ │ │ │
* * * * *  command-to-run

The job runs when the current time matches every field — with one important exception for the two day fields, covered below.

Special characters

CharacterMeaningExampleReads as
*Every value* * * * *Every minute
,List of values0 9,17 * * *At 09:00 and 17:00
-Range0 9 * * 1-5At 09:00, Monday to Friday
/Step*/15 * * * *Every 15 minutes

Steps can be combined with ranges: 0-30/10 in the minute field means minutes 0, 10, 20 and 30.

Many implementations also accept shortcuts:

ShortcutEquivalent
@hourly0 * * * *
@daily / @midnight0 0 * * *
@weekly0 0 * * 0
@monthly0 0 1 * *
@yearly / @annually0 0 1 1 *
@rebootOnce at startup (Linux cron only)

15 cron expressions you will actually use

*/5 * * * *       Every 5 minutes
0 * * * *         Every hour, on the hour
0 */6 * * *       Every 6 hours (00:00, 06:00, 12:00, 18:00)
0 0 * * *         Every day at midnight
30 2 * * *        Every day at 02:30
0 9 * * 1-5       Weekdays at 09:00
0 9-17 * * 1-5    Every hour from 09:00 to 17:00 on weekdays
0 0 * * 0         Every Sunday at midnight
0 0 * * 6,0       Saturdays and Sundays at midnight
0 0 1 * *         First day of every month at midnight
0 0 1 */3 *       First day of every quarter
0 0 1 1 *         Once a year, on January 1st
15 14 1 * *       14:15 on the 1st of every month
0 22 * * 1-5      Weeknights at 22:00
0 0 L * *         Last day of month (Quartz-style schedulers only — not standard cron)

You can paste any of these into the cron explainer tool to confirm the next run times.

The day-of-month vs day-of-week trap

This is the rule that surprises almost everyone. When both the day-of-month and day-of-week fields are restricted (neither is *), standard cron runs the job when either one matches, not both.

0 0 13 * 5

You might read that as "midnight on Friday the 13th". Classic cron reads it as "midnight on the 13th of every month and midnight every Friday". If you need a true "Friday the 13th" schedule, run the job on the 13th and check the weekday inside the script:

0 0 13 * * [ "$(date +\%u)" = 5 ] && /path/to/script.sh

Time zones

Cron uses the time zone of the machine (or the scheduler service) running it. A few practical consequences:

  • Servers usually run in UTC. A job set to 0 9 * * * on a UTC server runs at 9am UTC, which is 4am or 5am in New York depending on daylight saving time.
  • GitHub Actions schedules are always in UTC.
  • Daylight saving changes can skip or double-run jobs scheduled between 01:00 and 03:00 local time on systems that do not run in UTC. Schedule important jobs outside that window, or keep servers on UTC.

Check what time zone your system uses with:

timedatectl          # systemd-based Linux
date +%Z             # any Unix

Editing your crontab on Linux

crontab -e     # edit your user's crontab
crontab -l     # list current jobs
crontab -r     # remove ALL your jobs (careful: no confirmation)

Each line is one job. A real-world entry that backs up a database every night and logs the output looks like this:

30 2 * * * /home/deploy/bin/backup-db.sh >> /var/log/backup-db.log 2>&1

Common mistakes (and how to fix them)

1. The job works in the terminal but not in cron

Cron runs with a minimal environment: a short PATH, no shell profile, and often a different working directory. Use absolute paths for every command and file, or set PATH at the top of the crontab:

PATH=/usr/local/bin:/usr/bin:/bin

2. Unescaped percent signs

In a crontab, % is turned into a newline. Commands such as date +%Y-%m-%d must escape it as \%:

0 1 * * * tar czf /backups/site-$(date +\%F).tar.gz /var/www

3. No output, no idea what happened

Redirect both stdout and stderr to a log file (>> file.log 2>&1) so failures leave a trace. Without it, output is mailed to the local user — which on most servers means it disappears.

4. Confusing */N with "every N from now"

*/20 in the minute field means minutes 0, 20 and 40 of each hour — it is aligned to the clock, not to when you saved the crontab.

5. Overlapping runs

If a job sometimes takes longer than its interval, two copies can run at once. Use flock to prevent that:

*/5 * * * * flock -n /tmp/sync.lock /usr/local/bin/sync.sh

Cron in other tools

PlatformFieldsNotes
Linux crontab5Machine time zone
GitHub Actions5Always UTC; shortest interval is 5 minutes
Kubernetes CronJob5Supports a timeZone field
Quartz / Spring6–7Adds seconds (and optional year), supports L, W, #, ?
AWS EventBridge6Adds year; uses ? for one of the day fields

If an expression has six or seven fields, you are looking at a Quartz-style or cloud-provider dialect, not standard cron.

FAQ

What does * * * * * mean?

Run every minute of every hour of every day.

How do I run a cron job every 30 seconds?

Standard cron cannot go below one minute. Either run two entries, one with sleep 30 before the command, or use a systemd timer, which supports second-level precision.

Is Sunday 0 or 7?

Both. Most cron implementations accept 0 and 7 for Sunday, and SUN works as well.

How can I test a cron expression without waiting?

Paste it into a cron explainer to see the next run times, and run the command manually with env -i to mimic cron's minimal environment.

Further reading

Keep reading