DD
DevDash
cronlinuxdevopstutorial

Cron Expressions Explained: A Visual Guide with Examples

Cron expressions look like line noise the first time you see them. 0 9 1-5 -- what does that even mean? But the syntax is surprisingly simple once you learn the five fields. This guide will get you from confusion to confidence in about five minutes.

The five fields

A cron expression has five fields, separated by spaces:

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

Each field accepts:

  • A specific value: 5
  • A wildcard: * (every possible value)
  • A range: 1-5
  • A list: 1,3,5
  • A step: */5 (every 5th value)

That's it. Five fields, five operators. Everything else is just combinations.

Reading cron expressions

Read them left to right: "At minute X, of hour Y, on day Z..."

ExpressionMeaning
*Every minute
0 Every hour (at minute 0)
0 0 *Every day at midnight
0 9 *Every day at 9:00 AM
0 9 1-5Every weekday at 9:00 AM
0 0 1 First day of every month at midnight
0 0 1 1 *January 1st at midnight (once per year)

Common patterns with examples

Every N minutes

# Every 5 minutes
*/5 * * * *

# Every 15 minutes
*/15 * * * *

# Every 30 minutes
*/30 * * * *
# equivalent to:
0,30 * * * *

Specific times daily

# 9:00 AM every day
0 9 * * *

# 9:30 AM every day
30 9 * * *

# 6:00 PM every day
0 18 * * *

# Twice daily: 9 AM and 6 PM
0 9,18 * * *

Business hours

# Every weekday at 9 AM
0 9 * * 1-5

# Every hour during business hours, weekdays
0 9-17 * * 1-5

# Every 30 minutes during business hours, weekdays
0,30 9-17 * * 1-5

Weekly

# Every Monday at 9 AM
0 9 * * 1

# Every Sunday at midnight
0 0 * * 0

# Every Friday at 5 PM
0 17 * * 5

# Tuesday and Thursday at 10 AM
0 10 * * 2,4

Monthly

# First of every month at midnight
0 0 1 * *

# 15th of every month at noon
0 12 15 * *

# Last day of month (not directly supported - use alternatives below)
# This runs on 28th, which isn't always the last day:
0 0 28 * *

The "last day of month" problem is a known limitation of cron. Most cron implementations don't have a "last day" keyword. Workarounds include running a daily job that checks the date:

# Run at midnight every day, but the script checks if tomorrow is the 1st
0 0 * * * [ "$(date -d tomorrow +\%d)" = "01" ] && /path/to/script.sh

Quarterly

# First day of each quarter at 9 AM
0 9 1 1,4,7,10 *

Special characters in detail

The step operator (/)

*/N means "every Nth value, starting from the beginning of the range."

# Every 5 minutes: 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55
*/5 * * * *

# Every 3 hours: 0, 3, 6, 9, 12, 15, 18, 21
0 */3 * * *

# Starting at minute 5, then every 10 minutes: 5, 15, 25, 35, 45, 55
5/10 * * * *

The range operator (-)

# Weekdays only (Monday through Friday)
0 9 * * 1-5

# First 10 days of the month
0 0 1-10 * *

# Between 9 AM and 5 PM
0 9-17 * * *

The list operator (,)

# Specific minutes: 0, 15, 30, 45
0,15,30,45 * * * *

# Specific months: Jan, Apr, Jul, Oct
0 0 1 1,4,7,10 *

# Monday, Wednesday, Friday
0 9 * * 1,3,5

Setting up cron jobs

On Linux/macOS

# Edit your crontab
crontab -e

# Add a job (runs at 9 AM daily)
0 9 * * * /path/to/script.sh

# List current jobs
crontab -l

# Remove all jobs (careful!)
crontab -r

Important notes:

  • Cron jobs run in a minimal environment. Always use full paths for commands and files.
  • Set the PATH at the top of your crontab: PATH=/usr/local/bin:/usr/bin:/bin
  • Redirect output to a log file: 0 9 * /path/to/script.sh >> /var/log/myjob.log 2>&1
  • Cron uses the system timezone by default. Set CRON_TZ=UTC for explicit timezone control.

Common debugging steps

If your cron job isn't running:

  1. Check the system cron log: grep CRON /var/log/syslog
  2. Verify the crontab is saved: crontab -l
  3. Test the command manually with the full path
  4. Check file permissions (the script must be executable)
  5. Make sure environment variables are set in the crontab, not just your shell

Cron in the cloud

Traditional cron runs on a single server. If that server goes down, your job doesn't run. Cloud schedulers solve this with managed, distributed execution.

AWS EventBridge (formerly CloudWatch Events)

# AWS uses a 6-field format (adds year) with "rate" expressions too
cron(0 9 * * ? *)     # Daily at 9 AM UTC
rate(5 minutes)        # Every 5 minutes

Note the ? in the day-of-week field -- AWS requires either day-of-month or day-of-week to be ? (not both *).

Google Cloud Scheduler

# Standard cron syntax
gcloud scheduler jobs create http my-job \
  --schedule="0 9 * * *" \
  --uri="https://my-api.com/run" \
  --time-zone="America/New_York"

Google Cloud Scheduler supports standard 5-field cron with explicit timezone, which is a significant improvement over system cron.

GitHub Actions

on:
  schedule:
    - cron: '0 9 * * 1-5'  # Weekdays at 9 AM UTC

GitHub Actions cron is always UTC and has a minimum interval of 5 minutes. Jobs may run late during high-load periods.

Building and validating cron expressions

When you're constructing a complex cron expression, it helps to validate it before deploying. devdash.io/tools/cron-builder provides a visual builder where you can set each field and see the next 10 execution times. Useful for catching off-by-one errors before they hit production.

For command-line validation:

# Python - croniter library
pip install croniter
python3 -c "
from croniter import croniter
from datetime import datetime
cron = croniter('0 9 * * 1-5', datetime.now())
for _ in range(5):
    print(cron.get_next(datetime))
"

Quick reference

I want to run...Expression
Every minute *
Every 5 minutes/5 *
Every hour0
Every day at midnight0 0 *
Every day at 9 AM0 9 *
Weekdays at 9 AM0 9 1-5
Every Monday at 9 AM0 9 1
First of every month0 0 1
Every 6 hours0 /6
Every quarter (Jan, Apr, Jul, Oct)0 0 1 1,4,7,10 *

The syntax is simple. The hard part is remembering the field order. Minute, hour, day-of-month, month, day-of-week. Once that's muscle memory, you'll read cron expressions as naturally as regular expressions. Well, more naturally.

Related Tools

Want API access + no ads? Pro coming soon.