A
cd ..
System

Crontab Advanced

Advanced cron job scheduling, debugging, and best practices.

2025-11-08
cron, scheduling, automation

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"

# Check if already running
if [ -f "$LOCK_FILE" ]; then
    echo "Script is already running"
    exit 1
fi

# Create lock file
touch "$LOCK_FILE"

# Cleanup on exit
trap "rm -f $LOCK_FILE" EXIT

# Your actual script
/path/to/actual/script.sh

Debugging cron jobs

# Check cron service
sudo systemctl status cron
sudo systemctl status crond  # RHEL/CentOS

# Check cron logs
sudo tail -f /var/log/syslog | grep CRON
sudo tail -f /var/log/cron

# Enable cron logging (if not enabled)
# Edit /etc/rsyslog.conf or /etc/rsyslog.d/50-default.conf
# Uncomment: cron.*                          /var/log/cron.log

# Restart syslog
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

# System cron directories
/etc/cron.hourly/
/etc/cron.daily/
/etc/cron.weekly/
/etc/cron.monthly/

# Direct file edits
/etc/crontab
/etc/cron.d/

# Example /etc/crontab entry
0 2 * * * root /path/to/script.sh

Run-parts

# /etc/crontab uses run-parts
0 2 * * * root run-parts /etc/cron.daily

# Script in /etc/cron.daily/ must be:
# - Executable (chmod +x)
# - No file extension
# - Not contain dots in name

Anacron (for desktop/laptop)

# /etc/anacrontab
# period  delay  job-identifier  command
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

# Allow specific users
# Add to /etc/cron.allow
username

# Deny specific users
# Add to /etc/cron.deny
baduser

# If neither file exists, only root can use crontab

Environment in cron

# Cron has minimal environment
# Always use absolute paths

# Bad
* * * * * script.sh

# Good
* * * * * /usr/local/bin/script.sh

# Set PATH
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

# Backup
crontab -l > ~/crontab_backup.txt

# Restore
crontab ~/crontab_backup.txt

# Backup all users
for user in $(cut -f1 -d: /etc/passwd); do
    sudo crontab -l -u $user > crontab_${user}.bak 2>/dev/null
done

Monitor cron jobs

# List all user crontabs
for user in $(cut -f1 -d: /etc/passwd); do
    echo "=== $user ==="
    sudo crontab -l -u $user 2>/dev/null
done

# Check last execution
grep CRON /var/log/syslog | tail -20

Best practices

# 1. Always use absolute paths
0 2 * * * /usr/bin/python3 /home/user/script.py

# 2. Set email for errors
MAILTO=admin@example.com

# 3. Log everything
0 2 * * * /path/to/script.sh >> /var/log/script.log 2>&1

# 4. Use locking to prevent overlap
0 2 * * * flock -n /tmp/script.lock -c '/path/to/script.sh'

# 5. Set environment
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
HOME=/home/user

# 6. Test manually first
/path/to/script.sh

# 7. Start with frequent runs for testing
* * * * * /path/to/script.sh

# 8. Add comments
# Daily backup at 2 AM
0 2 * * * /usr/local/bin/backup.sh

Cron alternatives

# Systemd timers (modern alternative)
sudo systemctl list-timers

# At (one-time jobs)
echo "/path/to/script.sh" | at 2:00 AM tomorrow

# Jenkins (for complex workflows)
# Airflow (for data pipelines)

Was this useful?

Share with your team

Browse More