X Xerobit

Cron Expression Guide — Syntax, Fields, and Special Characters

Cron expressions schedule recurring jobs using five or six fields: minute, hour, day-of-month, month, day-of-week. Here's the full cron syntax including *, /, -, and ?...

Mian Ali Khalid · · 5 min read
Use the tool
Cron Builder
Build and parse cron expressions with human-readable explanations.
Open Cron Builder →

Cron expressions are strings of 5 or 6 fields that specify when a scheduled job should run. Used in Unix cron, GitHub Actions, AWS CloudWatch, Kubernetes CronJobs, and many schedulers.

Use the Cron Builder to build and validate cron expressions visually.

Standard 5-field cron format

┌───────────── minute (0–59)
│ ┌───────────── hour (0–23)
│ │ ┌───────────── day of month (1–31)
│ │ │ ┌───────────── month (1–12 or JAN–DEC)
│ │ │ │ ┌───────────── day of week (0–6 or SUN–SAT, 0=Sunday)
│ │ │ │ │
* * * * *

Special characters

CharacterMeaningExample
*Every value* * * * * = every minute
,List of values1,15 * * * * = at minute 1 and 15
-Range1-5 * * * * = minutes 1,2,3,4,5
/Step*/15 * * * * = every 15 minutes
?No specific value (day fields)0 12 * * ?
LLast (some schedulers)0 0 L * * = last day of month
#Nth occurrence (some schedulers)0 9 * * 2#1 = first Tuesday at 9am

Common cron expressions

# Every minute:
* * * * *

# Every 5 minutes:
*/5 * * * *

# Every 15 minutes:
*/15 * * * *

# Every hour (at :00):
0 * * * *

# Every 2 hours:
0 */2 * * *

# Every day at midnight:
0 0 * * *

# Every day at 6am:
0 6 * * *

# Every day at 6am and 6pm:
0 6,18 * * *

# Every weekday (Mon–Fri) at 9am:
0 9 * * 1-5

# Every Monday at 9am:
0 9 * * 1

# Every Sunday at midnight:
0 0 * * 0

# 1st of every month at midnight:
0 0 1 * *

# Every 3 months (quarterly), 1st at midnight:
0 0 1 */3 *

# Every January 1st at midnight:
0 0 1 1 *

# Weekdays every 30 minutes, 9am–5pm:
*/30 9-17 * * 1-5

6-field format (with seconds)

Some schedulers (Quartz, Spring, AWS EventBridge) use a 6-field format:

┌─────────────── second (0–59)
│ ┌───────────── minute (0–59)
│ │ ┌───────────── hour (0–23)
│ │ │ ┌───────────── day of month (1–31)
│ │ │ │ ┌───────────── month (1–12)
│ │ │ │ │ ┌───────────── day of week (0–6)
│ │ │ │ │ │
0 * * * * *      = every minute at :00 seconds
0 0 * * * *      = every hour at 0:00
0 0 9 * * 1-5   = weekdays at 9:00:00

GitHub Actions schedule syntax

GitHub Actions uses standard 5-field cron in UTC:

on:
  schedule:
    # Every day at 2am UTC:
    - cron: '0 2 * * *'
    
    # Every Monday at 9am UTC:
    - cron: '0 9 * * 1'
    
    # Every 6 hours:
    - cron: '0 */6 * * *'
    
    # First and fifteenth of each month:
    - cron: '0 0 1,15 * *'

jobs:
  scheduled-task:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Running scheduled job"

Note: GitHub Actions schedules are always UTC. Minimum interval is 5 minutes; schedules may run late during high load.

Kubernetes CronJob

apiVersion: batch/v1
kind: CronJob
metadata:
  name: backup-job
spec:
  schedule: "0 2 * * *"   # 2am daily
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: backup
              image: backup-tool:latest
              command: ["/backup.sh"]
          restartPolicy: OnFailure
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1

Node.js with node-cron

import cron from 'node-cron';

// Every 5 minutes:
cron.schedule('*/5 * * * *', () => {
  console.log('Running every 5 minutes');
});

// Weekdays at 9am:
cron.schedule('0 9 * * 1-5', () => {
  sendDailyReport();
}, {
  timezone: 'America/New_York',
});

// With error handling:
cron.schedule('0 0 * * *', async () => {
  try {
    await cleanupOldFiles();
  } catch (err) {
    console.error('Daily cleanup failed:', err);
  }
});

Python with APScheduler

from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger

scheduler = BackgroundScheduler()

# Every day at 2am:
scheduler.add_job(
    backup_database,
    CronTrigger(hour=2, minute=0),
    id='daily_backup'
)

# Every weekday at 9am:
scheduler.add_job(
    send_report,
    CronTrigger(day_of_week='mon-fri', hour=9, minute=0),
    id='weekday_report'
)

scheduler.start()

Cron gotchas

Day-of-month AND day-of-week are OR’d:

# This runs on the 1st of the month OR every Monday:
0 0 1 * 1

# To run ONLY on Monday the 1st:
# → Not directly expressible in standard cron (use application logic)

No second granularity in standard cron:

# To run every 30 seconds, you need two entries:
*/1 * * * *   # every minute at :00
*/1 * * * *   # can't do :30
# → Use a scheduler with second support, or sleep(30) inside the job

Timezones: Standard Unix cron runs in the server’s local timezone. Always specify timezone explicitly in application schedulers.


Related posts

Related tool

Cron Builder

Build and parse cron expressions with human-readable explanations.

Written by Mian Ali Khalid. Part of the Dev Productivity pillar.