The Complete Guide to Cron Job Scheduling
Cron is one of the most useful utilities in Linux and Unix-like systems. It lets you schedule tasks to run automatically at specific times or intervals, whether that means running a backup every night, clearing temp files every hour, or sending a report every Monday morning. If you have ever needed something to happen on a schedule without you being there to press the button, cron is the answer.
This guide will walk you through everything you need to know about cron job scheduling: the syntax, the commands, practical examples, and the mistakes that trip up nearly everyone at some point. You can test any cron expression from this guide using our Cron Expression Parser.
What Is Cron?
Cron is a time-based job scheduler built into virtually every Linux and Unix system. It runs as a background daemon (crond) that wakes up every minute, checks a list of scheduled tasks, and executes any that are due. Each scheduled task is called a cron job, and the file that holds a user's list of jobs is called a crontab (short for "cron table").
Cron has been around since the 1970s, and for good reason: it is simple, reliable, and does exactly one thing well. You describe when something should run using a compact expression, and cron handles the rest. There is no GUI, no cloud dependency, and no subscription. It just works.
Understanding Cron Expression Syntax
A cron expression is a string of five fields separated by spaces. Each field represents a unit of time, and together they define the schedule. The format is:
* * * * * command_to_run
| | | | |
| | | | +---- Day of week (0-7, where 0 and 7 are Sunday)
| | | +------ Month (1-12)
| | +-------- Day of month (1-31)
| +---------- Hour (0-23)
+------------ Minute (0-59)
The Five Fields Explained
| Field | Allowed Values | Description |
|---|---|---|
| Minute | 0–59 | The exact minute the job should run |
| Hour | 0–23 | The hour in 24-hour format (0 = midnight, 23 = 11 PM) |
| Day of Month | 1–31 | The calendar day (note: not all months have 31 days) |
| Month | 1–12 | The month of the year (1 = January, 12 = December) |
| Day of Week | 0–7 | The weekday (0 and 7 both represent Sunday, 1 = Monday) |
Each field can contain a single value, a range, a list, or a step value. An asterisk (*) means "every possible value" for that field.
Special Characters in Cron Expressions
Cron supports several special characters that give you flexibility beyond simple fixed values:
| Character | Meaning | Example |
|---|---|---|
* |
Any value (wildcard) | * * * * * — every minute |
, |
List of values | 0,15,30,45 * * * * — minutes 0, 15, 30, 45 |
- |
Range of values | 0 9-17 * * * — every hour from 9 AM to 5 PM |
/ |
Step values | */10 * * * * — every 10 minutes |
You can also combine these. For instance, 1-30/5 in the minute field means "every 5 minutes during the first half of each hour" (minutes 1, 6, 11, 16, 21, 26).
Practical Cron Expression Examples
Here are the most common schedules you will actually use in practice. Try pasting any of these into our Cron Expression Parser to see the next run times.
Every Minute
* * * * * /path/to/script.sh
Runs once per minute. Useful for monitoring checks or queue processing, but be careful with resource-heavy tasks.
Every 5 Minutes
*/5 * * * * /path/to/script.sh
A common choice for health checks, cache warming, or polling an API.
Every Hour (on the Hour)
0 * * * * /path/to/script.sh
Runs at minute 0 of every hour. Good for hourly log rotation or report generation.
Every Day at Midnight
0 0 * * * /path/to/backup.sh
The classic daily job. Runs at 00:00 every day. Commonly used for database backups, log cleanup, and daily digest emails.
Every Day at 6:30 AM
30 6 * * * /path/to/morning-report.sh
Useful for triggering reports or notifications before the workday starts.
Every Monday at 9 AM
0 9 * * 1 /path/to/weekly-task.sh
Weekly tasks like sending summary reports, weekly backups, or database maintenance.
Weekdays Only at 8 AM
0 8 * * 1-5 /path/to/workday-task.sh
Runs Monday through Friday. Perfect for business-hours tasks that should not run on weekends.
First Day of Every Month at 2 AM
0 2 1 * * /path/to/monthly-report.sh
Monthly tasks like generating invoices, billing reports, or archiving old data.
Every 15 Minutes During Business Hours
*/15 9-17 * * 1-5 /path/to/check.sh
Runs every 15 minutes, but only between 9 AM and 5 PM on weekdays. A practical pattern for monitoring systems that only matter during working hours.
Twice a Day (8 AM and 8 PM)
0 8,20 * * * /path/to/sync.sh
Uses a comma-separated list in the hour field to run at two specific times each day.
Every Sunday at 3 AM (Weekly Maintenance)
0 3 * * 0 /path/to/maintenance.sh
A typical slot for weekly maintenance: off-hours on a weekend to minimize disruption.
January 1st at Midnight (Yearly)
0 0 1 1 * /path/to/yearly-archive.sh
Runs once a year. Useful for annual log rotation or yearly summary generation.
Predefined Schedule Shortcuts
Most cron implementations support shorthand strings that replace the five-field expression:
| Shorthand | Equivalent | Description |
|---|---|---|
@reboot |
N/A | Run once at system startup |
@yearly |
0 0 1 1 * |
Once a year (midnight, January 1) |
@monthly |
0 0 1 * * |
Once a month (midnight, first day) |
@weekly |
0 0 * * 0 |
Once a week (midnight on Sunday) |
@daily |
0 0 * * * |
Once a day (midnight) |
@hourly |
0 * * * * |
Once an hour (at minute 0) |
These shortcuts are convenient and self-documenting. Using @daily immediately communicates intent better than 0 0 * * * does.
Essential Crontab Commands
You interact with cron through the crontab command. Here are the commands every developer needs to know:
View Your Crontab
crontab -l
Lists all cron jobs for the current user. If you have never created one, it will say "no crontab for [username]".
Edit Your Crontab
crontab -e
Opens your crontab in the system's default text editor (usually vi or nano). When you save and exit, cron automatically picks up the changes. There is no need to restart the daemon.
Remove Your Crontab
crontab -r
Deletes your entire crontab. Be careful with this one — it removes all jobs, not just one. There is no confirmation prompt.
Edit Another User's Crontab (as Root)
sudo crontab -u username -e
As root, you can view or edit another user's crontab. This is useful for managing service accounts that do not have interactive logins.
System-Wide Cron Jobs
Besides per-user crontabs, Linux has system-wide cron directories:
/etc/crontab— the system crontab file (includes a user field)/etc/cron.d/— directory for individual cron files/etc/cron.hourly/— scripts here run once per hour/etc/cron.daily/— scripts here run once per day/etc/cron.weekly/— scripts here run once per week/etc/cron.monthly/— scripts here run once per month
To use the .hourly, .daily, .weekly, or .monthly directories, simply drop an executable script into the appropriate folder. No cron expression needed — the system handles the scheduling.
Environment and Path Issues
This is where most cron beginners hit their first wall. Cron jobs do not run with the same environment as your interactive shell. Specifically:
- PATH is minimal. Cron typically sets PATH to
/usr/bin:/bin. Commands likenode,python3,docker, orawsmay not be found unless you use their full path. - No shell profile is loaded. Your
.bashrc,.bash_profile, and.profileare not sourced. Any environment variables or aliases you rely on will not exist. - No display. The
DISPLAYvariable is not set, so GUI applications will not work.
The safest approach is to use absolute paths for everything:
# Bad: relies on PATH
0 2 * * * python3 /home/user/backup.py
# Good: uses absolute path to the interpreter
0 2 * * * /usr/bin/python3 /home/user/backup.py
You can find the full path to any command with which:
which python3
# /usr/bin/python3
Alternatively, you can set the PATH at the top of your crontab:
PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin
0 2 * * * python3 /home/user/backup.py
Handling Output and Logging
By default, cron captures the standard output and standard error from every job and tries to email it to the user who owns the crontab. On many modern systems, local mail is not configured, so this output silently vanishes.
You should always redirect output explicitly:
Log to a File
# Redirect stdout and stderr to a log file
0 2 * * * /usr/bin/python3 /home/user/backup.py >> /var/log/backup.log 2>&1
Discard Output Completely
# Send everything to /dev/null (silent mode)
0 2 * * * /usr/bin/python3 /home/user/backup.py > /dev/null 2>&1
Log stdout and stderr Separately
0 2 * * * /home/user/backup.sh >> /var/log/backup.log 2>> /var/log/backup-errors.log
Always log output during development and testing. You can switch to silent mode once you are confident the job works correctly.
Troubleshooting Cron Jobs
When a cron job does not run or produces unexpected results, work through this checklist:
1. Check That Cron Is Running
systemctl status cron
# or on some systems:
systemctl status crond
If the service is not active, start it with sudo systemctl start cron.
2. Check the System Log
# Debian/Ubuntu
grep CRON /var/log/syslog
# CentOS/RHEL
grep CRON /var/log/cron
The system log will show you when cron attempted to run your job. If your job does not appear at all, the cron expression is wrong or the crontab was not saved correctly.
3. Verify the Crontab Was Saved
crontab -l
Make sure your job actually appears. A common mistake is editing the crontab in vi and exiting without saving (:q! instead of :wq).
4. Test the Command Manually
Copy the exact command from your crontab and run it in a minimal shell environment:
env -i /bin/sh -c '/usr/bin/python3 /home/user/backup.py'
This simulates the stripped-down environment that cron provides. If it fails here, it will fail in cron too.
5. Check File Permissions
The script must be executable and readable by the user whose crontab it is in:
chmod +x /home/user/backup.sh
ls -la /home/user/backup.sh
6. Check for Newline at End of File
This is a classic gotcha. The crontab must end with a newline character. If the last line of your crontab does not have a trailing newline, cron may silently ignore it. Most text editors add one automatically, but it is worth verifying if a job mysteriously refuses to run.
Best Practices for Cron Jobs
- Use absolute paths everywhere. For the interpreter, the script, and any files the script reads or writes.
- Always redirect output. Either log it to a file or discard it explicitly. Do not rely on cron mail.
- Add comments to your crontab. Lines starting with
#are comments. Use them to describe what each job does and when it was added. - Avoid overlapping jobs. If a job takes 10 minutes but is scheduled every 5 minutes, you will end up running multiple copies at once. Use a lock file or
flockto prevent this:*/5 * * * * /usr/bin/flock -n /tmp/myjob.lock /path/to/script.sh - Set the MAILTO variable. If local mail is configured, add
MAILTO=you@example.comat the top of your crontab to receive error notifications. - Do not edit /etc/crontab for user tasks. Use
crontab -efor personal jobs. Reserve/etc/crontaband/etc/cron.d/for system-level tasks. - Keep jobs idempotent. Your script should produce the same result whether it runs once or multiple times. This protects you if cron accidentally fires a job twice.
- Stagger job times. Avoid scheduling everything at exactly midnight (
0 0 * * *). Spread jobs out to reduce resource spikes. For example, run backups at 0:05, log rotation at 0:15, and reports at 0:30. - Use version control for scripts. Keep your cron scripts in a Git repository so you can track changes and roll back if something breaks.
- Test with a short interval first. When setting up a new job, temporarily schedule it to run every minute (
* * * * *). Once you confirm it works, change it to the real schedule.
Cron vs. Systemd Timers
Modern Linux systems running systemd offer an alternative to cron called systemd timers. Timers are more verbose to configure but offer advantages like built-in logging (via journald), dependency management, and better resource controls. However, cron remains the standard for most use cases because of its simplicity and universal availability.
Use cron when you need a quick, portable, and straightforward scheduled task. Consider systemd timers when you need advanced features like randomized delays, calendar-based scheduling with second precision, or tight integration with other systemd services.
Quick Reference Cheat Sheet
# Every minute
* * * * *
# Every 5 minutes
*/5 * * * *
# Every hour
0 * * * *
# Every day at midnight
0 0 * * *
# Every Monday at 9 AM
0 9 * * 1
# Weekdays at 8 AM
0 8 * * 1-5
# First of month at 2 AM
0 2 1 * *
# Every 15 min, business hours, weekdays
*/15 9-17 * * 1-5
# Twice a day (8 AM and 8 PM)
0 8,20 * * *
# On system reboot
@reboot
Next Steps
The best way to get comfortable with cron expressions is to practice reading and writing them. Head over to our Cron Expression Parser to test expressions and see exactly when they will run next. You can also explore our cheat sheets for more quick-reference developer resources.
Cron has been quietly running scheduled tasks on servers for over 50 years, and there is good reason it is still the default choice for most developers. Learn the five-field syntax, remember to use absolute paths, redirect your output, and you will have reliable task automation with almost zero overhead.