Whether you are automating server maintenance, scheduling data pipelines, or triggering CI/CD workflows, the cron expression is the universal language of time-based job scheduling. A cron expression generator helps you build these scheduling patterns visually, while a cron parser translates cryptic strings like 0 */6 * * 1-5 into human-readable descriptions. This comprehensive guide covers everything from the classic five-field cron syntax to practical cron job examples in JavaScript, Python, Bash, and cloud platforms. If you need to quickly create or decode a cron schedule, try our free tool.
Try our free online Cron Expression Generator & Parser tool instantly.
What Is a Cron Expression?
A cron expression is a compact string that defines a recurring schedule for automated tasks. The name comes from the Greek word chronos (time) and the Unix cron daemon, which has been executing scheduled jobs on Unix-like systems since the 1970s. Originally created by Ken Thompson for Version 7 Unix, cron has become the de facto standard for task scheduling across operating systems, programming languages, and cloud platforms.
In the classic Unix crontab format, a cron expression consists of five space-separated fields: minute hour day-of-month month day-of-week. Each field accepts numbers, wildcards (*), ranges, lists, and step values. For example, 30 9 * * 1-5 means "at 09:30 on every weekday." The crontab file (short for "cron table") is where users store these expressions alongside the commands to execute.
Today, cron expressions have expanded far beyond traditional Unix systems. Kubernetes CronJobs use them to schedule containerized workloads, GitHub Actions use them in workflow schedule triggers, AWS EventBridge (formerly CloudWatch Events) supports cron-based scheduling rules, and tools like Vercel cron let you trigger serverless functions on a schedule. Understanding cron syntax is an essential skill for any developer or DevOps engineer working with automated processes.
Cron Expression Syntax Explained
The standard cron expression uses five fields. Each field represents a unit of time and determines when the cron job fires. Here is the complete breakdown:
| Field | Allowed Values | Special Characters | Description |
|---|---|---|---|
| Minute | 0-59 | * , - / | Minute of the hour when the job runs |
| Hour | 0-23 | * , - / | Hour of the day (24-hour format) |
| Day of Month | 1-31 | * , - / ? L W | Day of the month (1-based) |
| Month | 1-12 or JAN-DEC | * , - / | Month of the year |
| Day of Week | 0-7 or SUN-SAT | * , - / ? L # | Day of the week (0 and 7 both mean Sunday) |
Special characters explained: The asterisk * matches every possible value for that field. A comma , separates a list of values (e.g., 1,3,5 means Monday, Wednesday, Friday). A hyphen - defines a range (e.g., 9-17 means hours 9 through 17). A slash / defines a step value (e.g., */5 in the minute field means every 5 minutes). The question mark ? is used in some implementations (like Quartz) to indicate "no specific value" for day-of-month or day-of-week fields.
The overall format is: ┌───────────── minute (0-59) │ ┌─────────── hour (0-23) │ │ ┌───────── day of month (1-31) │ │ │ ┌─────── month (1-12) │ │ │ │ ┌───── day of week (0-7) │ │ │ │ │ * * * * *. This five-field pattern is the heart of every cron schedule definition.
Cron Expression Examples Table
Here are the most common cron job examples you will encounter in production systems. Use this table as a quick reference when building your cron schedule. You can paste any of these into a cron expression generator to verify the schedule:
| Cron Expression | Description |
|---|---|
* * * * * | Every minute |
*/5 * * * * | Every 5 minutes |
*/10 * * * * | Every 10 minutes |
*/15 * * * * | Every 15 minutes |
*/30 * * * * | Every 30 minutes |
0 * * * * | Every hour (at minute 0) |
0 */2 * * * | Every 2 hours |
0 */6 * * * | Every 6 hours |
0 0 * * * | Daily at midnight (00:00) |
0 6 * * * | Daily at 6:00 AM |
0 12 * * * | Daily at noon (12:00) |
0 0 * * 0 | Every Sunday at midnight |
0 9 * * 1-5 | Weekdays at 9:00 AM |
0 17 * * 1-5 | Weekdays at 5:00 PM |
30 9 * * 1 | Every Monday at 9:30 AM |
0 0 1 * * | First day of every month at midnight |
0 0 15 * * | 15th of every month at midnight |
0 0 1 1 * | January 1st at midnight (yearly) |
0 0 1 */3 * | First day of every quarter |
0 8-17 * * 1-5 | Every hour from 8 AM to 5 PM on weekdays |
0 0,12 * * * | Twice daily at midnight and noon |
0 0 * * 1,3,5 | Mon, Wed, Fri at midnight |
15,45 * * * * | At minute 15 and 45 of every hour |
0 3 1-7 * 1 | First Monday of every month at 3:00 AM |
Code Examples for Cron Jobs
JavaScript / Node.js Cron Jobs
Node.js offers several excellent libraries for scheduling cron jobs. The most popular options include node-cron for simple scheduling, the cron npm package for more control, croner for a modern zero-dependency approach, and Bree for worker-thread-based job scheduling. Here are practical examples using each library:
// ===== node-cron (most popular) =====
// npm install node-cron
const cron = require('node-cron');
// Run every 5 minutes
cron.schedule('*/5 * * * *', () => {
console.log('Running task every 5 minutes');
// Your task logic here
});
// Run weekdays at 9:00 AM
cron.schedule('0 9 * * 1-5', () => {
console.log('Good morning! Running weekday task.');
}, {
timezone: 'America/New_York'
});
// Validate a cron expression
console.log(cron.validate('*/5 * * * *')); // true
console.log(cron.validate('60 * * * *')); // false
// ===== cron npm package =====
// npm install cron
const { CronJob } = require('cron');
const job = new CronJob(
'0 */6 * * *', // every 6 hours
function () {
console.log('Running every 6 hours');
},
null, // onComplete
true, // start immediately
'America/Chicago' // timezone
);
// ===== croner (modern, zero-dependency) =====
// npm install croner
import { Cron } from 'croner';
// Run every minute
const task = new Cron('* * * * *', () => {
console.log('Tick every minute');
});
// Run at specific time, get next run
const next = task.nextRun();
console.log('Next execution:', next);
// Stop the job
task.stop();
// ===== Bree (worker-thread scheduler) =====
// npm install bree
const Bree = require('bree');
const bree = new Bree({
jobs: [
{
name: 'backup',
cron: '0 2 * * *', // daily at 2 AM
path: './jobs/backup.js'
},
{
name: 'cleanup',
cron: '0 0 * * 0', // weekly on Sunday
path: './jobs/cleanup.js'
}
]
});
bree.start();Python Cron Job Scheduling
Python provides multiple approaches to cron job scheduling. The schedule library offers a human-friendly API, APScheduler provides enterprise-grade features, the python-crontab module manipulates system crontab files directly, and Celery Beat handles distributed task scheduling. Here are examples of each approach:
# ===== schedule library (human-friendly) =====
# pip install schedule
import schedule
import time
def backup_database():
print("Backing up database...")
def send_report():
print("Sending daily report...")
schedule.every(5).minutes.do(backup_database)
schedule.every().day.at("09:00").do(send_report)
schedule.every().monday.at("08:30").do(send_report)
while True:
schedule.run_pending()
time.sleep(1)
# ===== APScheduler (enterprise-grade) =====
# pip install apscheduler
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.triggers.cron import CronTrigger
scheduler = BlockingScheduler()
# Using cron expression syntax
@scheduler.scheduled_job(CronTrigger.from_crontab('*/10 * * * *'))
def cleanup_temp_files():
print("Cleaning temp files every 10 minutes")
# Using keyword arguments
@scheduler.scheduled_job('cron', hour=9, minute=0, day_of_week='mon-fri')
def weekday_report():
print("Weekday report at 9:00 AM")
# First Monday of each month at 3 AM
@scheduler.scheduled_job('cron', day='1-7', day_of_week='mon', hour=3)
def monthly_audit():
print("Monthly audit")
scheduler.start()
# ===== python-crontab (manage system crontab) =====
# pip install python-crontab
from crontab import CronTab
cron = CronTab(user='myuser')
# Create a new cron job
job = cron.new(
command='/usr/bin/python3 /opt/app/backup.py >> /var/log/backup.log 2>&1'
)
job.setall('0 2 * * *') # Daily at 2 AM
job.set_comment('Nightly database backup')
job.enable()
# List all cron jobs
for job in cron:
print(f"{job.slices} -> {job.command}")
cron.write() # Save changes
# ===== Celery Beat (distributed scheduling) =====
# pip install celery
# celeryconfig.py
from celery.schedules import crontab
beat_schedule = {
'cleanup-every-hour': {
'task': 'tasks.cleanup',
'schedule': crontab(minute=0, hour='*/1'),
},
'weekly-report': {
'task': 'tasks.send_report',
'schedule': crontab(hour=9, minute=0, day_of_week=1),
},
}Bash / Linux Crontab Commands
On Linux and macOS, the crontab command is the primary way to manage cron jobs. The crontab -e command opens your user crontab for editing, crontab -l lists current jobs, and system-wide cron jobs live in /etc/cron.d/. Here are essential crontab commands and configuration patterns:
# ===== Essential crontab commands =====
# Edit your user crontab
crontab -e
# List current cron jobs
crontab -l
# Remove all cron jobs (use with caution!)
crontab -r
# Edit another user's crontab (requires root)
sudo crontab -u www-data -e
# ===== Example crontab file =====
# Set environment variables at the top
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin
MAILTO=admin@example.com
TZ=UTC
# m h dom mon dow command
# Backup database every night at 2 AM
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
# Run cleanup every 15 minutes with flock
*/15 * * * * flock -n /tmp/cleanup.lock /opt/scripts/cleanup.sh
# Send report on weekdays at 9 AM
0 9 * * 1-5 /opt/scripts/daily-report.sh 2>&1 | logger -t daily-report
# Rotate logs on the 1st of each month
0 0 1 * * /usr/sbin/logrotate /etc/logrotate.conf
# Health check every minute (ping monitoring service)
* * * * * curl -fsS --retry 3 https://hc-ping.com/your-uuid > /dev/null
# ===== System crontab (/etc/cron.d/) =====
# /etc/cron.d/myapp (includes username field)
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
# Note: system crontab includes a user field
*/5 * * * * www-data /opt/myapp/cron/process-queue.sh
0 3 * * * root /opt/myapp/cron/maintenance.sh
# ===== Logging best practices =====
# Log with timestamp
0 * * * * /opt/scripts/job.sh 2>&1 | while read line; do \
echo "$(date '+\%Y-\%m-\%d \%H:\%M:\%S') $line"; \
done >> /var/log/myjob.log
# MAILTO for error notifications
MAILTO=ops-team@example.com
# Only errors are emailed (stdout goes to log, stderr to email)
0 */6 * * * /opt/scripts/etl.sh >> /var/log/etl.logCloud & CI/CD Cron Scheduling
Cron expressions power scheduled automation across all major cloud and CI/CD platforms. GitHub Actions uses them in workflow schedule triggers, AWS EventBridge (formerly CloudWatch Events) supports a slightly modified cron syntax, Kubernetes CronJob resources schedule containerized tasks, and Vercel cron triggers serverless functions. Here are configuration examples for each platform:
# ===== GitHub Actions scheduled workflow =====
# .github/workflows/scheduled.yml
name: Scheduled Tasks
on:
schedule:
# Run every day at 6 AM UTC
- cron: '0 6 * * *'
# Run every Monday at 9 AM UTC
- cron: '0 9 * * 1'
workflow_dispatch: # Allow manual trigger
jobs:
daily-task:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./scripts/daily-task.sh
---
# ===== AWS EventBridge (CloudWatch) cron =====
# Note: AWS cron has 6 fields (includes year)
# Format: minute hour day-of-month month day-of-week year
# Every 5 minutes
aws events put-rule \
--name "every-5-min" \
--schedule-expression "cron(*/5 * * * ? *)"
# Weekdays at 9 AM UTC
aws events put-rule \
--name "weekday-morning" \
--schedule-expression "cron(0 9 ? * MON-FRI *)"
# First day of each quarter
aws events put-rule \
--name "quarterly" \
--schedule-expression "cron(0 0 1 1,4,7,10 ? *)"
---
# ===== Kubernetes CronJob =====
# k8s-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: database-backup
spec:
schedule: "0 2 * * *" # Daily at 2 AM
timeZone: "America/New_York" # Available since k8s 1.27
concurrencyPolicy: Forbid # Prevent overlapping runs
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: myapp/backup:latest
command: ["/bin/sh", "-c", "./backup.sh"]
env:
- name: DB_HOST
valueFrom:
secretKeyRef:
name: db-credentials
key: host
restartPolicy: OnFailure
---
# ===== Vercel cron (vercel.json) =====
{
"crons": [
{
"path": "/api/cron/daily-cleanup",
"schedule": "0 0 * * *"
},
{
"path": "/api/cron/hourly-sync",
"schedule": "0 * * * *"
},
{
"path": "/api/cron/weekly-report",
"schedule": "0 9 * * 1"
}
]
}Advanced Cron Features
While the standard five-field cron expression covers most scheduling needs, many implementations extend the syntax with additional fields and special characters. The most common extension is a seconds field (6-field format) prepended before the minute field: second minute hour day-of-month month day-of-week. Libraries like Quartz (Java), node-cron, and Spring Framework support this extended format. Some implementations also add a seventh field for year.
Special characters in extended cron: The L character means "last" - L in the day-of-month field means the last day of the month, while 5L in the day-of-week field means the last Friday. The W character means "nearest weekday" - 15W means the nearest weekday to the 15th. The # character specifies the Nth occurrence of a weekday - 2#3 means the third Monday of the month (2=Monday, 3=third occurrence). These are supported by Quartz-based schedulers.
Non-standard shorthand expressions are convenient aliases recognized by most cron implementations: @reboot runs the job once at system startup; @yearly or @annually is equivalent to 0 0 1 1 *; @monthly equals 0 0 1 * *; @weekly equals 0 0 * * 0; @daily or @midnight equals 0 0 * * *; and @hourly equals 0 * * * *. These make crontab entries more readable without needing a crontab guru reference.
Cron Job Debugging & Monitoring
Timezone issues are the most common source of cron job failures. By default, cron uses the system timezone, which can vary between servers, containers, and cloud environments. Always explicitly set the TZ environment variable in your crontab (e.g., TZ=UTC) or use UTC-based scheduling to avoid surprises when daylight saving time changes or when deploying to different regions. In Kubernetes CronJobs, set the timeZone field (available since v1.27) to your desired IANA timezone.
PATH and environment issues: The cron daemon runs jobs with a minimal environment, not your shell's full environment. The default PATH in cron is typically just /usr/bin:/bin, which means commands like node, python3, or docker may not be found. Fix this by setting PATH at the top of your crontab, using absolute paths for all commands (e.g., /usr/local/bin/node), or sourcing your profile at the start of the script.
Output not captured: By default, cron tries to email the output of each job using the local mail system. If mail is not configured (common on modern systems), output is silently lost, making debugging nearly impossible. Always redirect stdout and stderr to a log file: 0 * * * * /path/to/script.sh >> /var/log/myjob.log 2>&1. Set MAILTO=your@email.com at the top of the crontab to receive email notifications on job failures.
Overlapping runs: If a cron job takes longer to execute than the interval between runs, multiple instances may run simultaneously, causing resource contention, data corruption, or deadlocks. Use a lock file mechanism with flock (e.g., flock -n /tmp/myjob.lock /path/to/script.sh) to prevent overlapping execution. Monitoring tools like healthchecks.io, Cronitor, and Dead Man's Snitch can alert you when a job fails to run, runs too long, or overlaps.
Cron Job Best Practices
Use descriptive comments: Add a comment above each crontab entry explaining what the job does, who owns it, and when it was last modified. Comments start with # in crontab. This is critical for team environments where multiple engineers maintain the same crontab. Without comments, a cron expression like 0 3 * * 0 is meaningless to someone unfamiliar with the project.
Redirect output and use logging: Never let cron output go to /dev/null unless you are absolutely certain the job is reliable. Instead, redirect to a dedicated log file with timestamps: 0 * * * * /path/to/job.sh >> /var/log/cronjobs/job.log 2>&1. Use logrotate to prevent log files from growing indefinitely. Include timestamps in your script output for easier debugging.
Use flock for mutual exclusion: Prevent overlapping runs with the flock command. Add it to your crontab entry: */5 * * * * flock -n /tmp/myjob.lock /path/to/script.sh. The -n flag makes it non-blocking so the new instance simply exits if the lock is held. This is especially important for jobs that run frequently (every minute, every 5 minutes) where execution time may vary.
Set SHELL and PATH explicitly: At the top of your crontab, declare SHELL=/bin/bash (or your preferred shell) and PATH=/usr/local/bin:/usr/bin:/bin (including all directories needed by your scripts). This prevents failures caused by the minimal default cron environment. Also set MAILTO to receive notifications.
Use absolute paths everywhere: In cron jobs, always use absolute paths for scripts, binaries, configuration files, and output files. Relative paths are resolved against the user's home directory or root, which is rarely what you want. Instead of python script.py, use /usr/bin/python3 /opt/myapp/script.py.
Monitor with dedicated services: Use monitoring services like healthchecks.io, Cronitor, or Dead Man's Snitch to track job execution. These services provide a unique URL that your cron job pings upon completion. If the ping is missed, you get alerted. This catches silent failures that log-based monitoring might miss. Add a simple curl call at the end of your script: curl -fsS --retry 3 https://hc-ping.com/your-uuid.
Be timezone-aware: Document the timezone your cron jobs expect. Use TZ=UTC at the top of your crontab for consistency across environments. Be aware that daylight saving time transitions can cause jobs scheduled between 2:00 and 3:00 AM to be skipped or run twice. Use a cron expression generator or crontab guru to verify your schedules account for these edge cases.
Frequently Asked Questions
What do the five fields in a cron expression mean?
The five fields in a standard cron expression represent, from left to right: minute (0-59), hour (0-23), day of month (1-31), month (1-12 or JAN-DEC), and day of week (0-7 or SUN-SAT, where both 0 and 7 mean Sunday). Each field accepts exact values, wildcards (*), ranges (1-5), lists (1,3,5), and step values (*/5). For example, "30 9 * * 1-5" means at 09:30 on every weekday (Monday through Friday).
What is the difference between cron and systemd timers?
Cron is the traditional Unix job scheduler using crontab files with five-field expressions. Systemd timers are the modern alternative on Linux systems using systemd. Key differences: systemd timers can trigger on events (not just time), support calendar-based schedules with more natural syntax, handle missed runs with Persistent=true, integrate with systemd logging (journalctl), and support per-unit resource limits. However, cron expressions remain the standard in cloud platforms (AWS, GitHub Actions, Kubernetes) and cross-platform tools. For simple time-based scheduling, cron is sufficient; for complex requirements on systemd-based Linux, consider systemd timers.
How do I run a cron job every N minutes?
To run a cron job every N minutes, use the step syntax */N in the minute field. Examples: every 5 minutes = "*/5 * * * *", every 10 minutes = "*/10 * * * *", every 15 minutes = "*/15 * * * *", every 30 minutes = "*/30 * * * *". Note that the step value must evenly divide 60 for consistent intervals. For example, */7 runs at minutes 0, 7, 14, 21, 28, 35, 42, 49, and 56 - then jumps back to 0 (only 4 minutes later). For truly even intervals that do not divide 60, consider using a loop-based approach or a task scheduler like systemd timers.
Related guides:
- Cron Expression Examples: Common Patterns & Cheat Sheet
- Cron Scheduling in Serverless: GitHub Actions, Vercel & Cloudflare
Mastering cron expressions is essential for any developer or operations engineer who needs reliable, time-based automation. From basic Unix crontab configurations to cloud-native cron schedules in Kubernetes and GitHub Actions, the five-field cron syntax is a universal skill. Use a cron expression generator to build schedules visually and a cron parser to verify them. Bookmark this guide as your comprehensive reference.
Generate and parse cron expressions instantly with our free online tool.