Cron Expression Guide: Master Linux Task Scheduling
Learn how to write, understand, and debug cron expressions. Includes a visual builder, common examples, and troubleshooting tips for developers.
Introduction: Why Cron Matters for Developers
Every developer eventually needs to run code on a schedule. Whether it's sending weekly reports, cleaning up old database records, refreshing cached data, or running nightly backups — cron is the standard solution on Linux and macOS systems. Understanding cron expressions is a fundamental skill that will serve you throughout your career.
In this comprehensive guide, you'll learn not just the syntax, but the practical patterns and debugging techniques that separate beginners from experienced developers. We'll cover everything from basic scheduling to environment gotchas that can ruin otherwise perfect cron jobs.
What is Cron?
Cron is a time-based job scheduler that has been running on Unix-like systems since the 1970s. The name comes from the Greek word "chronos" (time). It's designed to run commands or scripts automatically at specified intervals — hourly, daily, weekly, or custom schedules.
The cron daemon (cron or crond) runs continuously in the background, waking up every minute to check if any scheduled jobs need execution. When a match is found, the command runs in a subshell with a minimal environment.
Where Cron Jobs are Defined
Cron jobs can be defined in several locations:
- User crontab: Stored in
/var/spool/cron/crontabs/(Unix) or~/.crontab. Edit withcrontab -e. - System crontab:
/etc/crontaband files in/etc/cron.d/. These run as root or specific users. - Scheduled tasks:
/etc/cron.daily/,/etc/cron.hourly/, etc. (system directories).
The Cron Expression Format
A cron expression consists of five fields, separated by spaces. Each field represents a time component:
* * * * *
│ │ │ │ │
│ │ │ │ └── Day of week (0–7, Sunday = 0 or 7)
│ │ │ └──── Month (1–12)
│ │ └────── Day of month (1–31)
│ └──────── Hour (0–23)
└────────── Minute (0–59)Field Values
Each field accepts specific value types:
- Number: Exact value (e.g.,
5for 5th minute) - List: Comma-separated values (e.g.,
1,15,30) - Range: Hyphen-separated values (e.g.,
9-17for 9am through 5pm) - Step: Asterisk with increment (e.g.,
*/15for every 15 units) - Wildcard:
*means "every" value
Special Characters in Detail
The Asterisk (*)
The asterisk matches any value. * * * * * means "every minute of every hour of every day." This is rarely useful alone but forms the foundation for other patterns.
Commas (,) for Lists
Commas create lists of values. 0 9,12,18 * * * runs at 9am, noon, and 6pm every day. You can combine ranges and lists: 0 9-17,19 * * * runs every hour from 9am to 5pm and also at 7pm.
Hyphens (-) for Ranges
Hyphens define inclusive ranges. 30 9-17 * * 1-5 runs at half past every hour from 9:30am to 5:30pm, Monday through Friday — perfect for business-hour reminders.
Slashes (/) for Steps
Slashes define intervals. */15 * * * * runs every 15 minutes. 0 */2 * * * runs at midnight, 2am, 4am, and so on. You can also use steps within ranges: 0-30/10 * * * * runs at 0, 10, 20, and 30 minutes past each hour.
Common Cron Expression Examples
Every Minute, Hour, Day
# Every minute
* * * * *
# Every hour (at minute 0)
0 * * * *
# Every day at midnight
0 0 * * *
# Every day at 2:30am
30 2 * * *Weekly Schedules
# Every Monday at 9am
0 9 * * 1
# Monday through Friday at 8am
0 8 * * 1-5
# Every Saturday at 6pm
0 18 * * 6
# Weekdays only (alternative syntax)
0 9 * * Mon,Tue,Wed,Thu,FriMonthly Schedules
# First day of every month at midnight
0 0 1 * *
# Last day of every month at 11pm
0 23 L * *
# 15th of every month at noon
0 12 15 * *
# First Monday of every month
0 9 * * 1#1Quarterly and Custom Schedules
# Every 15 minutes
*/15 * * * *
# Every 6 hours
0 */6 * * *
# Every Sunday at 3am
0 3 * * 0
# Every 2 hours during business hours
0 9-17/2 * * *Real-World Cron Job Examples
Database Backups
# Daily backup at 2am
0 2 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1
# Weekly backup on Sunday at 3am
0 3 * * 0 /opt/scripts/full-backup.shLog Rotation
# Rotate logs every day at midnight
0 0 * * * /usr/sbin/logrotate /etc/logrotate.conf
# Clean old logs every Sunday at 4am
0 4 * * 0 find /var/log -name "*.log" -mtime +30 -deleteReport Generation
# Generate daily report at 7am
0 7 * * * python /opt/reports/daily.py
# Weekly report every Monday at 8am
0 8 * * 1 python /opt/reports/weekly-summary.pyCache Management
# Clear expired cache every hour
0 * * * * rm -rf /tmp/cache/*
# Refresh CDN cache every 6 hours
0 */6 * * * /opt/scripts/refresh-cdn.shHealth Checks
# Check service health every 5 minutes
*/5 * * * * /opt/monitoring/health-check.sh
# Send daily uptime report at 9am
0 9 * * * /opt/monitoring/uptime-report.shEnvironment Variables and Best Practices
Setting the PATH
Cron runs with a minimal environment, often missing your usual PATH. Always use absolute paths:
# Wrong - may fail
* * * * * myscript.sh
# Correct - full path
* * * * * /usr/local/bin/myscript.sh
# Better - set PATH at the top
PATH=/usr/local/bin:/usr/bin:/bin
0 * * * * myscript.shRedirecting Output
Cron doesn't have a terminal, so all output goes to email by default (if configured). Always redirect output:
# Redirect both stdout and stderr to log file
0 * * * * /opt/scripts/job.sh >> /var/log/job.log 2>&1
# Discard output entirely
0 * * * * /opt/scripts/job.sh > /dev/null 2>&1
# Log stdout, email stderr
0 * * * * /opt/scripts/job.sh >> /var/log/job.logLock Files to Prevent Overlap
If your cron job might run longer than its interval, use a lock file to prevent overlap:
# Check if previous run is still executing
0 * * * * [ -f /tmp/job.lock ] || /opt/scripts/job.shUser-Specific Cron Settings
# Set shell and PATH at the top of crontab
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=admin@example.com
# Then add jobs
0 * * * * /opt/scripts/hourly-task.shTroubleshooting Cron Issues
Debugging with Dry Runs
Before creating a cron job, test your command manually:
# Test in foreground first
/usr/local/bin/myscript.sh
# Check exit code
echo $?
# Verify paths exist
which myscript.shCommon Problems and Solutions
- Command not found: Use full paths or set PATH at crontab top
- Module not found: Set working directory or use absolute paths in Python/Ruby scripts
- Permission denied: Check file permissions and ownership
- Job doesn't run: Check
systemctl status crondandgrep CRON /var/log/syslog - Missing environment: Use absolute paths and explicit environment variables
Useful Commands
# List your crontab
crontab -l
# Edit your crontab
crontab -e
# Remove all crontab entries
crontab -r
# View system cron log
grep CRON /var/log/syslog
# Check cron service status
systemctl status cron # Ubuntu/Debian
systemctl status crond # CentOS/RHELQuartz Cron Format (For Java/Spring Developers)
If you work with Java frameworks like Spring, you might encounter Quartz-format cron expressions. These add a seconds field:
# Quartz format: seconds minute hour day month weekday
0 30 9 * * ? # 9:30:00 every day
0 0 12 1 * ? # 12:00:00 on 1st of every month
0 0 9 ? * MON-FRI # 9:00:00 Monday through FridayOur Cron Generator supports both standard Unix cron and Quartz formats.
Summary
Cron expressions follow a consistent five-field format that, once understood, makes complex scheduling intuitive. Remember these key points:
- Five fields: minute, hour, day-of-month, month, day-of-week
- Use special characters:
*(any),,(list),-(range),/(step) - Always use absolute paths in commands
- Redirect output to log files to avoid lost emails
- Test commands manually before adding to cron
With this knowledge, you can schedule anything from simple hourly tasks to complex business-hour schedules with confidence.
Frequently Asked Questions
Why doesn't my cron job run?
Top 5 reasons: (1) Wrong PATH — cron has /usr/bin:/bin only, not your shell's PATH. (2) Missing executable bit — chmod +x the script. (3) Environment differences — variables from .bashrc aren't loaded. (4) Permissions — the cron user can't read files. (5) Syntax error — try running the exact same command from a non-interactive shell to reproduce.
What's the difference between crontab and /etc/cron.d?
User crontabs (crontab -e) are stored per-user in /var/spool/cron/. They run as that user. /etc/cron.d/ contains system crontabs that specify a user explicitly (root run-parts /etc/cron.daily). Use crontab -e for personal jobs; use /etc/cron.d/ for system services managed by packages.
How do I run a cron job every 30 seconds?
Cron can't do sub-minute intervals directly. Workarounds: run every minute and sleep 30s at the start (sleep 30 && /path/to/job), or use a more capable scheduler like systemd timers, fcron, or a job queue. For most use cases, 1-minute resolution is plenty.
