Edit crontab
crontab -e
List cron jobs
crontab -l
Remove all cron jobs
crontab -r
Edit other user's crontab
sudo crontab -u username -e
Crontab syntax
* * * * * command
│ │ │ │ │
│ │ │ │ └─ Day of week (0-7, 0 and 7 = Sunday)
│ │ │ └─── Month (1-12)
│ │ └───── Day of month (1-31)
│ └─────── Hour (0-23)
└───────── Minute (0-59)
Common schedules
# Every minute
* * * * * command
# Every 5 minutes
*/5 * * * * command
# Every hour at minute 0
0 * * * * command
# Every day at midnight
0 0 * * * command
# Every day at 2:30 AM
30 2 * * * command
# Every Sunday at midnight
0 0 * * 0 command
# Every weekday at 6 PM
0 18 * * 1-5 command
# First day of month at midnight
0 0 1 * * command
# Every quarter (Jan, Apr, Jul, Oct)
0 0 1 */3 * command
# Twice a day (8 AM and 8 PM)
0 8,20 * * * command
Special strings
@reboot # Run at startup
@yearly # Run once a year (0 0 1 1 *)
@annually # Same as @yearly
@monthly # Run once a month (0 0 1 * *)
@weekly # Run once a week (0 0 * * 0)
@daily # Run once a day (0 0 * * *)
@midnight # Same as @daily
@hourly # Run once an hour (0 * * * *)
Environment variables
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=admin@example.com
HOME=/home/user
0 2 * * * /path/to/script.sh
Logging output
# Redirect to file
0 2 * * * /path/to/script.sh >> /var/log/script.log 2>&1
# Redirect stdout and stderr separately
0 2 * * * /path/to/script.sh >> /var/log/script.log 2>> /var/log/script.error
# Suppress output
0 2 * * * /path/to/script.sh > /dev/null 2>&1
# Email on error only
0 2 * * * /path/to/script.sh > /dev/null
Prevent overlapping
# Using flock
* * * * * flock -n /tmp/script.lock -c '/path/to/script.sh'
# Using lockfile
* * * * * lockfile -r 0 /tmp/script.lock && /path/to/script.sh; rm -f /tmp/script.lock
Script wrapper for cron
#!/bin/bash
LOCK_FILE="/tmp/backup.lock"
if [ -f "$LOCK_FILE" ]; then
echo "Script is already running"
exit 1
fi
touch "$LOCK_FILE"
trap "rm -f $LOCK_FILE" EXIT
/path/to/actual/script.sh
Debugging cron jobs
sudo systemctl status cron
sudo systemctl status crond
sudo tail -f /var/log/syslog | grep CRON
sudo tail -f /var/log/cron
sudo systemctl restart rsyslog
Test cron job
# Run every minute for testing
* * * * * echo "Test at $(date)" >> /tmp/crontest.log 2>&1
# Check output
tail -f /tmp/crontest.log
# Remove after testing
crontab -e # Delete the test line
System-wide crontabs
/etc/cron.hourly/
/etc/cron.daily/
/etc/cron.weekly/
/etc/cron.monthly/
/etc/crontab
/etc/cron.d/
0 2 * * * root /path/to/script.sh
Run-parts
0 2 * * * root run-parts /etc/cron.daily
Anacron (for desktop/laptop)
1 5 cron.daily run-parts /etc/cron.daily
7 10 cron.weekly run-parts /etc/cron.weekly
30 15 cron.monthly run-parts /etc/cron.monthly
User cron permissions
username
baduser
Environment in cron
* * * * * script.sh
* * * * * /usr/local/bin/script.sh
PATH=/usr/local/bin:/usr/bin:/bin
* * * * * script.sh
Complex schedule examples
# Every 15 minutes during business hours
*/15 9-17 * * 1-5 /path/to/script.sh
# Every 2 hours
0 */2 * * * /path/to/script.sh
# At 4:30 AM on 1st and 15th
30 4 1,15 * * /path/to/script.sh
# Monday through Friday at 7 PM
0 19 * * 1-5 /path/to/script.sh
# Every 10 minutes between 9 AM - 5 PM
*/10 9-17 * * * /path/to/script.sh
# Last day of month (requires workaround)
0 0 28-31 * * [ $(date -d tomorrow +\%d) -eq 1 ] && /path/to/script.sh
Backup crontab
crontab -l > ~/crontab_backup.txt
crontab ~/crontab_backup.txt
for user in $(cut -f1 -d: /etc/passwd); do
sudo crontab -l -u $user > crontab_${user}.bak 2>/dev/null
done
Monitor cron jobs
for user in $(cut -f1 -d: /etc/passwd); do
echo "=== $user ==="
sudo crontab -l -u $user 2>/dev/null
done
grep CRON /var/log/syslog | tail -20
Best practices
0 2 * * * /usr/bin/python3 /home/user/script.py
MAILTO=admin@example.com
0 2 * * * /path/to/script.sh >> /var/log/script.log 2>&1
0 2 * * * flock -n /tmp/script.lock -c '/path/to/script.sh'
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
HOME=/home/user
/path/to/script.sh
* * * * * /path/to/script.sh
0 2 * * * /usr/local/bin/backup.sh
Cron alternatives
sudo systemctl list-timers
echo "/path/to/script.sh" | at 2:00 AM tomorrow