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 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

Daily Schedules

Weekly Schedules

Monthly and Yearly Schedules

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:

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:

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

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:

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