What Is a Cron Job in Linux? Complete Guide to Scheduled Tasks
In Linux, a cron job is the core tool for automating scheduled tasks. Once configured, the system automatically executes backups, log cleanup, service monitoring, and other repetitive tasks at specified times โ greatly improving operational efficiency. This guide covers everything from cron fundamentals, time format syntax, and practical examples to debugging tips.
What Is a Cron Job?
Cron is a built-in scheduling utility in Unix-like systems (including Linux). Its name derives from the Greek word “chronos” (time). The cron daemon runs continuously in the background, checking the schedule configuration once every minute and automatically executing the corresponding commands or scripts when the time conditions are met.
Common use cases for cron jobs:
- Automated backups: Back up databases or important files every night at 3 AM
- Log cleanup: Periodically delete logs older than 30 days to prevent disk space exhaustion
- System monitoring: Check whether services are running normally every few minutes
- Report generation: Automatically generate analytics reports weekly and send them via email
- Certificate renewal: Automatically renew SSL certificates (e.g., Let’s Encrypt’s certbot)
Cron Job vs systemd Timer
Modern Linux systems (especially Ubuntu 20.04+) also offer systemd timers as an alternative to cron. Each approach has its own strengths and weaknesses:
| Comparison | Cron Job | systemd Timer |
|---|---|---|
| Configuration difficulty | Simple, single-line setup | More complex, requires two config files |
| Logging | Must manually redirect to log files | Automatically integrated with journald |
| Missed execution handling | Missed runs are simply skipped | Can be configured to run after boot (OnBootSec) |
| Dependency management | Not supported | Supports systemd service dependencies |
| Best suited for | Quick, simple scheduled tasks | Complex service management scheduling |
For most everyday scheduling needs, cron job syntax is simple and effective, making it the most commonly used choice.
Crontab Time Format
The cron job time format consists of five fields followed by a command:
* * * * * command to execute
โ โ โ โ โ
โ โ โ โ โโโ Day of Week (0-7, both 0 and 7 represent Sunday)
โ โ โ โโโโโ Month (1-12)
โ โ โโโโโโโ Day of Month (1-31)
โ โโโโโโโโโ Hour (0-23)
โโโโโโโโโโโ Minute (0-59)
Field Reference
| Field | Description | Allowed Values |
|---|---|---|
| MIN | Minute | 0 to 59 |
| HOUR | Hour | 0 to 23 |
| DOM | Day of Month | 1 to 31 |
| MON | Month | 1 to 12, or English abbreviations (Jan, Feb, Mar…) |
| DOW | Day of Week | 0 (Sunday) to 6 (Saturday), 7 also means Sunday, or English abbreviations (Sun, Mon…) |
| CMD | Command to execute | Any executable program or script (with arguments) |
Special Characters
In addition to numeric values, cron supports the following special characters for more flexible scheduling:
| Character | Name | Description | Example |
|---|---|---|---|
* | Asterisk | Every possible value | * in the minute field = every minute |
, | Comma | List multiple values | 1,15,30 = at minute 1, 15, and 30 |
- | Hyphen | Specify a range | 9-17 = from 9 AM to 5 PM |
/ | Slash | Specify step intervals | */5 = every 5 units |
Crontab Cheat Sheet
| Schedule Description | Crontab Expression |
|---|---|
| Every minute | * * * * * |
| Every 5 minutes | */5 * * * * |
| Every 15 minutes | */15 * * * * |
| Every hour on the hour | 0 * * * * |
| Every day at 2:00 AM | 0 2 * * * |
| Every day at 8:30 AM | 30 8 * * * |
| Every Monday at 9:00 AM | 0 9 * * 1 |
| Every Monday, Wednesday, and Friday at 1:00 AM | 0 1 * * 1,3,5 |
| 1st of every month at midnight | 0 0 1 * * |
| Last day of month (approximate) at midnight | 0 0 28-31 * * |
| January 1st at midnight (yearly) | 0 0 1 1 * |
| Weekdays (MonโFri) at 8:00 AM | 0 8 * * 1-5 |
Crontab Commands
Basic Commands
# Edit the current user's crontab (opens the default editor)
crontab -e
# List all scheduled jobs for the current user
crontab -l
# Remove all scheduled jobs for the current user (dangerous! back up first)
crontab -r
# Back up the current crontab to a file
crontab -l > ~/crontab_backup.txt
# Restore crontab from a backup file
crontab ~/crontab_backup.txt
Managing Other Users’ Crontabs
# Edit a specific user's crontab as root
sudo crontab -u username -e
# View a specific user's scheduled jobs
sudo crontab -u username -l
# Remove a specific user's scheduled jobs
sudo crontab -u username -r
Crontab Guru โ Visual Cron Expression Editor
Crontab Guru- A handy tool that lets you quickly edit and preview the meaning of cron time expressions in real time โ perfect for beginners unfamiliar with the syntax
Practical Scheduling Examples
Basic Examples
# Run a backup script every day at 3:00 AM
0 3 * * * /home/benz/scripts/backup.sh
# Delete logs older than 30 days every Monday at 2:00 AM
0 2 * * 1 find /var/log/app -name "*.log" -mtime +30 -delete
# Log disk usage every hour
0 * * * * df -h >> /var/log/disk_usage.log
# Check if nginx is running every 5 minutes; restart if it's not
*/5 * * * * systemctl is-active nginx || systemctl restart nginx
# Send a daily system status report email at 8:00 AM
0 8 * * * /home/benz/scripts/daily_report.sh | mail -s "Daily Report" admin@example.com
Advanced Examples (Logging and Error Handling)
By default, cron discards command output (or sends it via email through MAILTO). It is recommended to redirect both stdout and stderr to a log file for easier troubleshooting:
# Redirect both stdout and stderr to a log file
0 3 * * * /home/benz/scripts/backup.sh >> /var/log/cron_backup.log 2>&1
# 2>&1 means: redirect stderr (2) to wherever stdout (1) points
# >> appends to the file without overwriting existing content
# Log only errors
0 3 * * * /home/benz/scripts/backup.sh > /dev/null 2>> /var/log/cron_errors.log
# Add timestamps to log entries
0 * * * * echo "$(date '+%Y-%m-%d %H:%M:%S') - Execution completed" >> /var/log/hourly.log
Setting Environment Variables in Cron
# Set environment variables at the top of the crontab (applies to all jobs)
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=admin@example.com
# With MAILTO set, cron will automatically send an email whenever a job produces output
# Set it to an empty string to disable email notifications:
MAILTO=""
0 3 * * * /home/benz/scripts/backup.sh
Cron Job Environment Variables
This is the most common “gotcha” with cron jobs โ a command that runs perfectly in your terminal fails when placed in cron. The cause is almost always different environment variables.
Why Are Cron’s Environment Variables Different from the Terminal?
When you log into a terminal, the system loads configuration files like .bashrc and .bash_profile, which set up environment variables such as PATH, JAVA_HOME, and others. However, cron runs in a non-interactive, minimal environment that does not load these configuration files, resulting in a very short PATH:
# PATH in your terminal (long, includes many custom paths)
echo $PATH
# /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/home/benz/.local/bin
# Default PATH in cron (very short)
# /usr/bin:/bin
Solutions
Method 1: Set PATH at the top of your crontab (Recommended)
# Add these lines at the top of crontab -e
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SHELL=/bin/bash
# All subsequent jobs will use this PATH
0 3 * * * backup.sh
Method 2: Use absolute paths in your commands
# Don't write
0 3 * * * node /home/benz/app/index.js
# Use the full path instead
0 3 * * * /usr/local/bin/node /home/benz/app/index.js
Method 3: Source environment configuration in your script
#!/bin/bash
# Load bash environment configuration at the start of the script
source /home/benz/.bashrc
# or
source /etc/profile
# Then run the actual logic
/usr/local/bin/node /home/benz/app/index.js
Cron Job Debugging Tips
When a scheduled job does not execute as expected, the following methods can help identify the problem:
1. Check Cron Logs in the System Journal
# View cron logs on Ubuntu/Debian systems
grep CRON /var/log/syslog | tail -50
# Or use journalctl (systemd-based systems)
journalctl -u cron --since "1 hour ago"
# Monitor cron logs in real time
tail -f /var/log/syslog | grep CRON
2. Verify the Cron Daemon Is Running
# Check the cron service status
systemctl status cron
# If it's not running, start it
sudo systemctl start cron
sudo systemctl enable cron # Enable auto-start on boot
3. Test Commands in the Same Environment
# Simulate cron's execution environment for testing
env -i PATH=/usr/bin:/bin HOME=/home/benz SHELL=/bin/bash /home/benz/scripts/backup.sh
# Verify the script has execute permissions
ls -l /home/benz/scripts/backup.sh
chmod +x /home/benz/scripts/backup.sh
4. Common Issues Checklist
| Problem | Likely Cause | Solution |
|---|---|---|
| Cron does not run at all | cron service is not started | systemctl start cron |
| Command not found | PATH does not include the command’s directory | Set PATH at the top of crontab or use absolute paths |
| Script has no execute permission | File is missing the x permission | chmod +x script.sh |
| File or directory not found | Relative paths are used | Use absolute paths (e.g., /home/benz/data/) |
| Runs but has no effect | Missing environment variables | Add source ~/.bashrc in the script |
System-Level Cron Configuration
In addition to user-level crontab, Linux provides several system-level scheduling locations:
/etc/crontab โ System-Wide Schedule
The /etc/crontab format includes an additional user field compared to user crontabs:
# /etc/crontab format (includes a username field)
# m h dom mon dow user command
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
/etc/cron.d/ โ Package-Specific Schedules
Packages often place their own schedule configurations in /etc/cron.d/, using the same format as /etc/crontab:
# List all system-level scheduled jobs
ls /etc/cron.d/
# Common entries include certbot (SSL certificate renewal)
cat /etc/cron.d/certbot
/etc/cron.daily/, cron.weekly/, cron.monthly/
These three directories contain scripts that the system automatically runs daily, weekly, or monthly:
ls /etc/cron.daily/
ls /etc/cron.weekly/
ls /etc/cron.monthly/
# Manually trigger a test run (simulate daily schedule execution)
run-parts /etc/cron.daily
anacron โ For Computers That Are Not Always On
If a computer is not running 24/7 (such as a laptop), cron may miss scheduled jobs while the machine is off. anacron compensates by running missed jobs after the system boots:
# View anacron configuration
cat /etc/anacrontab
# Format explanation:
# 1 5 cron.daily run-parts --report /etc/cron.daily
# period delay job-id command
Frequently Asked Questions (FAQ)
Q1: Should I use cron job or systemd timer?
For most simple scheduling needs, cron job syntax is intuitive and quick to set up โ making it the go-to choice. Consider systemd timers only if you need:
- Precise logging (integrated with journald)
- Task dependencies (a job must run only after a specific service starts)
- Missed job recovery after boot (similar to anacron functionality)
Q2: Why does my cron job run fine in the terminal but fail when scheduled?
The three most common causes:
- PATH issue: Cron’s PATH is very short and cannot find your command. Solution: add
PATH=...at the top of your crontab or use full paths in commands. - Permission issue: The script does not have execute permission (
chmod +x script.sh), or it accesses resources that require sudo. - Relative path issue: The script uses relative paths, but cron’s working directory may not be what you expect. Use absolute paths instead.
Q3: Cron can only go down to one-minute intervals. Can I run something every second?
Cron’s minimum resolution is one minute โ it cannot execute jobs every second. If you need per-second execution, consider these alternatives:
- Loop within a script: Write
while true; do command; sleep 1; donein a script, then use cron to launch it - systemd timer: Use
OnCalendar=*:*:0/5to achieve execution every 5 seconds - Background daemon service: For long-term needs, consider writing a systemd service that runs continuously
Further reading: