Cron Expression Syntax Explained with Examples

Finally make sense of those five mysterious asterisks

⏰ πŸ—“οΈ πŸ€– βš™οΈ

TL;DR: Cron expressions are five fields (minute, hour, day-of-month, month, day-of-week) that tell a computer when to run a task. * * * * * means "every minute," 0 9 * * 1 means "every Monday at 9 AM." Watch out for timezone gotchas and the weird OR-logic on day fields.

You know that feeling when you stare at */15 9-17 * * 1-5 and your brain just... freezes? You're not alone. Cron expressions look like someone's password, but they're actually a beautifully compact way to schedule recurring tasks. Let's decode them together.

The Five Magic Fields

A cron expression is just five fields separated by spaces. That's it. Here's the cheat sheet you'll want to bookmark:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ 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)
β”‚ β”‚ β”‚ β”‚ β”‚
* * * * *
πŸ–οΈ Five fields, five fingers β€” that's all you need to remember

The Special Characters Toolbox

Asterisk (*) β€” "I don't care, any value works." So * * * * * means "every minute of every hour of every day." (Please don't run your database backup every minute.)

Comma (,) β€” "Multiple specific values." 1,15 * * * * = "at minute 1 AND minute 15 of every hour."

Hyphen (-) β€” "A range." 0 9-17 * * * = "at the top of every hour from 9 AM to 5 PM."

Slash (/) β€” "Every Nth interval." */15 * * * * = "every 15 minutes" (runs at :00, :15, :30, :45). You can combine it with ranges too: 0-30/10 * * * * = "every 10 minutes during the first half hour."

The Patterns You'll Actually Use

Let's skip the theory and jump to the expressions developers copy-paste most often:

# Every minute (health checks, queue processing)
* * * * *

# Every 5 minutes (API polling, cache warming)
*/5 * * * *

# Every hour on the hour (reports, aggregation)
0 * * * *

# Every day at midnight (backups, log rotation)
0 0 * * *

# Every Monday at 9 AM (weekly team digest)
0 9 * * 1

# First day of the month at 6 AM (billing, invoices)
0 6 1 * *

# Weekdays at 8:30 AM (standup reminders)
30 8 * * 1-5

# Every 6 hours (certificate checks, data sync)
0 */6 * * *

# Twice a day β€” 8 AM and 8 PM (digest emails)
0 8,20 * * *

# Every 15 min during business hours on weekdays
*/15 9-17 * * 1-5
πŸ˜„

Dev Joke: A developer's cron tab is like their browser bookmarks β€” it starts organized and ends up looking like a crime scene within 6 months.

Cron in the Wild: Different Environments

Good Old Linux Crontab

Edit your crontab with crontab -e:

# Backup database every night at 2 AM
0 2 * * * /usr/local/bin/backup-db.sh >> /var/log/backup.log 2>&1

# Clean temp files every Sunday at 3 AM
0 3 * * 0 find /tmp -type f -mtime +7 -delete
πŸ’‘

Pro Tip: That 2>&1 redirects error output into the log file too. Without it, cron tries to email errors β€” which usually fails silently. Always redirect output in crontab!

GitHub Actions

name: Nightly Tests
on:
  schedule:
    - cron: '0 2 * * *'  # 2 AM UTC daily

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm test

Heads up: GitHub Actions uses UTC exclusively. If your team's in IST (UTC+5:30), a "9 AM IST" trigger would be 30 3 * * *. Time zones will burn you if you're not careful!

Kubernetes CronJob

apiVersion: batch/v1
kind: CronJob
metadata:
  name: cleanup-job
spec:
  schedule: "0 */4 * * *"  # Every 4 hours
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: cleanup
            image: myapp:latest
            command: ["node", "cleanup.js"]
          restartPolicy: OnFailure

AWS EventBridge

AWS is the rebel β€” it uses a 6-field format with a year field and requires ? in either the day-of-month or day-of-week position:

# AWS format: min hour day-of-month month day-of-week year
cron(0 9 ? * MON-FRI *)
🌍 Same concept, slightly different syntax everywhere β€” the eternal joy of computing

The Gotchas That'll Ruin Your Morning

1. Timezone Confusion

Cron runs in the server's timezone unless you explicitly set TZ or CRON_TZ. Deploy to a server in UTC, write your cron for local time, and wonder why nothing fires when expected. Always verify with timedatectl.

2. The Sneaky OR Logic

This one trips up everyone. If you set BOTH day-of-month and day-of-week to non-* values, the job runs when either condition is true (OR), not both (AND). So 0 0 15 * 1 runs on the 15th of every month AND every Monday β€” not just "the 15th if it's a Monday."

⚠️

Classic Trap: Cron jobs run with a minimal environment. Your PATH might not include /usr/local/bin. Always use full paths to executables, or set PATH at the top of your crontab.

3. Overlapping Runs

If your job takes longer than the interval between runs, you'll get multiple instances stacking up. Use flock to prevent this:

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

Shorthand Aliases (Where Supported)

@yearly   β†’ 0 0 1 1 *    (once a year, Jan 1 at midnight)
@monthly  β†’ 0 0 1 * *    (first day of each month)
@weekly   β†’ 0 0 * * 0    (every Sunday at midnight)
@daily    β†’ 0 0 * * *    (every day at midnight)
@hourly   β†’ 0 * * * *    (every hour at minute 0)
@reboot   β†’              (runs once at startup)

These work in most Linux crontab implementations but NOT in GitHub Actions, Kubernetes, or AWS. Stick with the five-field format for portability.

πŸ“‹ Copy this cheat sheet β€” your future self at 2 AM will thank you

Wrapping Up

Cron expressions are deceptively simple β€” five fields and a handful of special characters. But timezone issues, OR-logic on day fields, and platform variations can absolutely trip you up at the worst possible moment. The best habit? Always test your expressions before deploying them to production. And when in doubt, parse it visually.

Try It Yourself

Paste any cron expression into our parser and see the next scheduled run times instantly β€” no installation needed.

Open Cron Parser →