Cron Expressions Explained: Syntax, Examples, and Common Patterns
· 12 min read
Cron is the time-based job scheduler that powers automation on Unix-like operating systems. Whether you're backing up databases, sending scheduled emails, or running maintenance scripts, understanding cron expressions is essential for any developer or system administrator.
This comprehensive guide breaks down everything you need to know about cron syntax, from basic patterns to advanced scheduling techniques. You'll learn how to write, test, and troubleshoot cron expressions with confidence.
Table of Contents
- Understanding Cron Basics
- Cron Syntax: The Five Fields
- Special Characters and Operators
- Common Cron Patterns
- Advanced Scheduling Techniques
- Managing Your Crontab
- Environment Variables and Paths
- Common Pitfalls and How to Avoid Them
- Debugging Cron Jobs
- Modern Alternatives to Cron
- Frequently Asked Questions
- Related Articles
Understanding Cron Basics
Cron is a daemon process that runs continuously in the background of Unix-like systems, checking every minute whether there are scheduled jobs to execute. The name comes from the Greek word "chronos," meaning time.
Each user on a system can have their own crontab (cron table) file containing scheduled jobs. The system also maintains crontabs for system-level tasks in directories like /etc/cron.d/, /etc/cron.daily/, and /etc/cron.hourly/.
When a cron job executes, it runs with the permissions of the user who owns the crontab. This is crucial for security and file access considerations. The job runs in a minimal environment without your usual shell configuration, which is a common source of confusion for beginners.
Pro tip: Cron jobs don't have access to your interactive shell environment. Always use absolute paths for commands and files, and explicitly set any required environment variables in your crontab.
Cron Syntax: The Five Fields
A standard cron expression consists of five time-and-date fields followed by the command to execute. Understanding these fields is fundamental to writing effective cron schedules.
ββββββββββββββ minute (0-59)
β ββββββββββββββ hour (0-23)
β β ββββββββββββββ day of month (1-31)
β β β ββββββββββββββ month (1-12 or JAN-DEC)
β β β β ββββββββββββββ day of week (0-7, 0 and 7 = Sunday, or SUN-SAT)
β β β β β
* * * * * command to execute
Each field accepts specific values and special characters that control when the job runs. Let's break down what each field represents:
| Field | Range | Special Characters | Description |
|---|---|---|---|
| Minute | 0-59 | * , - / | The exact minute when the job runs |
| Hour | 0-23 | * , - / | Hour in 24-hour format (0 = midnight) |
| Day of Month | 1-31 | * , - / L W | The day of the month |
| Month | 1-12 or JAN-DEC | * , - / | Month of the year (1 = January) |
| Day of Week | 0-7 or SUN-SAT | * , - / L # | Day of the week (0 and 7 = Sunday) |
The command field can be any valid shell command, script path, or series of commands separated by semicolons. Output from cron jobs is typically emailed to the user unless redirected.
Special Characters and Operators
Cron's power comes from its special characters that allow flexible scheduling patterns. Mastering these operators lets you create sophisticated schedules without complex logic.
The Asterisk (*) - Match All Values
The asterisk is the wildcard character that matches all possible values for a field. When you use * in a field, the job runs for every value in that field's range.
For example, * * * * * means "every minute of every hour of every day of every month on every day of the week" - in other words, every single minute.
The Comma (,) - List Multiple Values
Commas let you specify multiple discrete values. This is useful when you need specific, non-consecutive times.
Example: 0 9,12,15,18 * * * runs at 9 AM, noon, 3 PM, and 6 PM every day.
The Hyphen (-) - Define Ranges
Hyphens create inclusive ranges of values. This is cleaner than listing many consecutive values with commas.
Example: 0 9-17 * * 1-5 runs every hour from 9 AM to 5 PM, Monday through Friday (business hours).
The Slash (/) - Step Values
The slash operator specifies step intervals. The syntax is */n or range/n where n is the step value.
Example: */15 * * * * runs every 15 minutes (at :00, :15, :30, :45). You can also use ranges: 0-30/5 * * * * runs every 5 minutes during the first half of each hour.
Extended Characters (L, W, #)
These are non-standard extensions supported by some cron implementations like Vixie cron and the Quartz scheduler, but not by standard Unix cron.
| Character | Meaning | Example | Description |
|---|---|---|---|
L |
Last | 0 0 L * * |
Last day of the month at midnight |
W |
Weekday | 0 0 15W * * |
Nearest weekday to the 15th |
# |
Nth occurrence | 0 0 * * 5#3 |
Third Friday of every month |
Quick tip: Before using L, W, or # characters, verify your cron implementation supports them. Standard Unix cron will reject these expressions. Use our Cron Expression Generator to test compatibility.
Common Cron Patterns
Here are the most frequently used cron patterns you'll encounter in real-world applications. These cover the majority of scheduling needs for typical automation tasks.
Basic Time Intervals
* * * * *- Every minute (use sparingly, can create high load)*/5 * * * *- Every 5 minutes*/10 * * * *- Every 10 minutes*/15 * * * *- Every 15 minutes (common for monitoring scripts)*/30 * * * *- Every 30 minutes0 * * * *- Every hour on the hour0 */2 * * *- Every 2 hours0 */4 * * *- Every 4 hours0 */6 * * *- Every 6 hours (four times daily)
Daily Schedules
0 0 * * *- Daily at midnight (common for backups and reports)0 2 * * *- Daily at 2:00 AM (low-traffic time for maintenance)0 9 * * *- Daily at 9:00 AM (business hours start)30 4 * * *- Daily at 4:30 AM0 0,12 * * *- Twice daily at midnight and noon0 8,20 * * *- Twice daily at 8 AM and 8 PM
Weekly Schedules
0 0 * * 0- Weekly on Sunday at midnight0 0 * * 1- Weekly on Monday at midnight0 9 * * 1-5- Weekdays at 9:00 AM0 0 * * 6,0- Weekends (Saturday and Sunday) at midnight30 4 * * 1- Every Monday at 4:30 AM (weekly reports)0 8-17 * * 1-5- Every hour during business hours (9 AM - 5 PM, Mon-Fri)
Monthly and Yearly Schedules
0 0 1 * *- First day of every month at midnight0 0 15 * *- 15th of every month at midnight0 0 1,15 * *- 1st and 15th of each month (bi-monthly)0 0 * * 0- Every Sunday (weekly)0 0 1 1 *- January 1st at midnight (yearly)0 0 1 */3 *- First day of every quarter (Jan, Apr, Jul, Oct)0 0 1 6,12 *- June 1st and December 1st (bi-annually)
Pro tip: When scheduling monthly jobs, be careful with day-of-month values above 28. Using 0 0 31 * * will only run in months with 31 days. For end-of-month jobs, consider using the last day of the previous month or the first day of the next month instead.
Test and validate your cron expressions using our Cron Expression Parser to see exactly when your jobs will run.
Advanced Scheduling Techniques
Beyond basic patterns, you can combine operators and fields to create sophisticated schedules that match complex business requirements.
Combining Multiple Conditions
You can use multiple operators in a single field to create nuanced schedules. For example, 0 9,12,15 * * 1-5 runs at 9 AM, noon, and 3 PM, but only on weekdays.
Another example: */10 8-17 * * 1-5 runs every 10 minutes, but only during business hours (8 AM to 5 PM) on weekdays.
Day of Month vs Day of Week
When both day-of-month and day-of-week are specified (not *), the job runs when either condition is met (OR logic, not AND). This is a common source of confusion.
For example, 0 0 13 * 5 runs at midnight on the 13th of every month OR every Friday, not just on Friday the 13th.
To schedule something specifically for Friday the 13th, you'd need to use a script that checks both conditions:
0 0 13 * * [ $(date +\%u) -eq 5 ] && /path/to/script.sh
Using Step Values with Ranges
You can combine ranges and step values for precise control. The syntax start-end/step runs at intervals within a specific range.
Examples:
0-30/5 * * * *- Every 5 minutes during the first half of each hour (:00, :05, :10, :15, :20, :25, :30)0 9-17/2 * * *- Every 2 hours from 9 AM to 5 PM (9 AM, 11 AM, 1 PM, 3 PM, 5 PM)0 0 1-7 * 1- First Monday of every month (runs on the 1st through 7th if it's a Monday)
Real-World Scheduling Examples
Here are practical examples that demonstrate how to solve common scheduling challenges:
Database backup every 6 hours:
0 */6 * * * /usr/local/bin/backup-database.sh
Clear temporary files daily at 3 AM:
0 3 * * * find /tmp -type f -mtime +7 -delete
Send weekly report every Monday at 8 AM:
0 8 * * 1 /usr/local/bin/generate-weekly-report.sh
Health check every 2 minutes during business hours:
*/2 9-17 * * 1-5 /usr/local/bin/health-check.sh
Monthly invoice generation on the first day at 6 AM:
0 6 1 * * /usr/local/bin/generate-invoices.sh
Managing Your Crontab
The crontab command is your primary interface for managing scheduled jobs. Understanding these commands is essential for effective cron management.
Basic Crontab Commands
# Edit your crontab (opens in default editor)
crontab -e
# List current crontab entries
crontab -l
# Remove all crontab entries (use with caution!)
crontab -r
# Edit another user's crontab (requires root)
sudo crontab -u username -e
# List another user's crontab
sudo crontab -u username -l
Crontab File Format
Your crontab file can include comments (lines starting with #) and environment variable definitions. Here's a well-structured example:
# Environment variables
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
[email protected]
# Database backups every 6 hours
0 */6 * * * /usr/local/bin/backup-database.sh
# Clear logs daily at 2 AM
0 2 * * * find /var/log/myapp -name "*.log" -mtime +30 -delete
# Weekly report every Monday at 9 AM
0 9 * * 1 /usr/local/bin/weekly-report.sh
# Health check every 5 minutes
*/5 * * * * /usr/local/bin/health-check.sh
System-Wide Cron Directories
Most Linux distributions provide convenient directories for system-wide cron jobs that don't require cron syntax:
/etc/cron.hourly/- Scripts run every hour/etc/cron.daily/- Scripts run once per day/etc/cron.weekly/- Scripts run once per week/etc/cron.monthly/- Scripts run once per month/etc/cron.d/- Additional crontab files with full cron syntax
Scripts placed in these directories must be executable (chmod +x script.sh) and should not have file extensions.
Quick tip: When editing your crontab with crontab -e, the changes take effect immediately upon saving. You don't need to restart the cron daemon. However, syntax errors will prevent the crontab from being saved, protecting you from breaking your scheduled jobs.
Environment Variables and Paths
One of the most common sources of cron job failures is the minimal environment in which they run. Unlike your interactive shell, cron jobs don't load your .bashrc, .profile, or other shell configuration files.
Setting Environment Variables
You can define environment variables at the top of your crontab file. These apply to all jobs in that crontab:
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
HOME=/home/username
[email protected]
# Your cron jobs here
0 * * * * /usr/local/bin/my-script.sh
Important Environment Variables
SHELL- The shell to use for executing commands (default:/bin/sh)PATH- Directories to search for commands (default is very minimal)HOME- Home directory for the userMAILTO- Email address to send job output (empty string disables email)LOGNAME- Username of the crontab owner
Using Absolute Paths
Always use absolute paths in your cron jobs to avoid "command not found" errors:
# Bad - relies on PATH
0 0 * * * backup.sh
# Good - absolute path
0 0 * * * /usr/local/bin/backup.sh
# Also good - explicit path to interpreter
0 0 * * * /usr/bin/python3 /home/user/scripts/backup.py
Redirecting Output
By default, cron emails all output from jobs. You can redirect output to files or discard it:
# Redirect stdout to a log file
0 0 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
# Discard all output
0 0 * * * /usr/local/bin/cleanup.sh > /dev/null 2>&1
# Keep errors but discard normal output
0 0 * * * /usr/local/bin/script.sh > /dev/null
Common Pitfalls and How to Avoid Them
Even experienced developers encounter issues with cron jobs. Here are the most common problems and their solutions.
1. Percent Signs in Commands
Percent signs (%) have special meaning in crontab - they're converted to newlines. If your command uses percent signs (like in date formatting), you must escape them:
# Wrong - will fail
0 0 * * * /usr/local/bin/backup-$(date +%Y%m%d).sh
# Correct - escape the percent signs
0 0 * * * /usr/local/bin/backup-$(date +\%Y\%m\%d).sh
# Alternative - use a wrapper script
0 0 * * * /usr/local/bin/daily-backup.sh
2. Missing PATH Variables
Commands that work in your terminal might fail in cron because the PATH is different. Always use absolute paths or set PATH explicitly:
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 0 * * * node /home/user/app/script.js
3. Day of Month and Day of Week Interaction
When both fields are specified (not *), the job runs when either condition matches (OR logic). This catches many people off guard:
# This runs on the 1st of every month OR every Monday
# NOT just on Mondays that fall on the 1st
0 0 1 * 1 /usr/local/bin/script.sh
# To run only on Mondays that are the 1st, use:
0 0 1-7 * 1 /usr/local/bin/script.sh
4. Timezone Confusion
Cron uses the system's timezone. If your server is in UTC but you want jobs to run in your local timezone, you have several options:
- Set the
TZvariable in your crontab:TZ=America/New_York - Change the system timezone (requires root access)
- Calculate the UTC equivalent time for your schedule
- Use a wrapper script that handles timezone conversion
5. Overlapping Job Executions
If a job takes longer than its interval, multiple instances might run simultaneously. Prevent this with lock files:
#!/bin/bash
LOCKFILE=/var/lock/myjob.lock
if [ -e $LOCKFILE ]; then
echo "Job already running"
exit 1
fi
touch $LOCKFILE
trap "rm -f $LOCKFILE" EXIT
# Your actual job commands here
/usr/local/bin/long-running-task.sh
6. Silent Failures
C