Cron is the standard Linux scheduler. It runs commands at specified times ā backups at midnight, log rotation weekly, health checks every minute.
Crontab Syntax
āāāāāāāāāāāāāā minute (0-59)
ā āāāāāāāāāāāāāā hour (0-23)
ā ā āāāāāāāāāāāāāā day of month (1-31)
ā ā ā āāāāāāāāāāāāāā month (1-12)
ā ā ā ā āāāāāāāāāāāāāā day of week (0-7, 0 and 7 = Sunday)
ā ā ā ā ā
* * * * * commandCommon Schedules
# Every minute
* * * * * /path/to/script.sh
# Every 5 minutes
*/5 * * * * /path/to/script.sh
# Every hour at minute 0
0 * * * * /path/to/script.sh
# Daily at 2:30 AM
30 2 * * * /path/to/script.sh
# Monday to Friday at 9 AM
0 9 * * 1-5 /path/to/script.sh
# First day of every month at midnight
0 0 1 * * /path/to/script.sh
# Every 15 minutes during business hours
*/15 9-17 * * 1-5 /path/to/script.sh
# Twice daily (8 AM and 8 PM)
0 8,20 * * * /path/to/script.sh
# Sunday at 3 AM (weekly maintenance)
0 3 * * 0 /path/to/script.shMaster this topic with hands-on labs
Go beyond reading ā build real projects in sandboxed environments with expert video guidance.
Browse Courses āManaging Crontabs
# Edit your crontab
crontab -e
# List your crontab
crontab -l
# Edit another user's crontab (root)
crontab -e -u www-data
# Remove your crontab (careful!)
crontab -r
# Remove with confirmation
crontab -riWriting Reliable Cron Scripts
Always Use Full Paths
Cron has a minimal environment:
#!/bin/bash
# Bad ā cron can't find commands
pg_dump mydb > backup.sql
# Good ā full paths
/usr/bin/pg_dump mydb > /backups/db-$(date +\%Y\%m\%d).sqlSet PATH Explicitly
# At the top of your crontab
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SHELL=/bin/bash
MAILTO=admin@example.com
0 2 * * * /opt/scripts/backup.shLogging
# Redirect stdout and stderr to a log file
0 2 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1
# With timestamp
0 2 * * * /opt/scripts/backup.sh 2>&1 | while read line; do echo "$(date '+\%Y-\%m-\%d \%H:\%M:\%S') $line"; done >> /var/log/backup.log
# Suppress output (only errors via MAILTO)
0 2 * * * /opt/scripts/backup.sh > /dev/nullPrevent Overlapping Runs
# Using flock (best method)
* * * * * /usr/bin/flock -n /tmp/myjob.lock /opt/scripts/long-running.sh
# Flock with timeout (wait up to 60s for lock)
* * * * * /usr/bin/flock -w 60 /tmp/myjob.lock /opt/scripts/process.shError Handling in Scripts
#!/bin/bash
set -euo pipefail
LOG="/var/log/backup.log"
BACKUP_DIR="/backups"
DATE=$(date +%Y%m%d_%H%M%S)
log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG"; }
log "Starting backup"
if /usr/bin/pg_dump -U postgres mydb > "$BACKUP_DIR/db-$DATE.sql"; then
log "Database backup completed"
else
log "ERROR: Database backup failed"
exit 1
fi
# Cleanup old backups (keep 7 days)
find "$BACKUP_DIR" -name "db-*.sql" -mtime +7 -delete
log "Cleanup completed"Systemd Timers (Modern Alternative)
Timer Unit
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily backup timer
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.targetService Unit
# /etc/systemd/system/backup.service
[Unit]
Description=Database backup
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
User=backup
Nice=10
IOSchedulingClass=idleEnable and Monitor
sudo systemctl enable --now backup.timer
# List all timers
systemctl list-timers --all
# Check last run
systemctl status backup.service
# View logs
journalctl -u backup.service --since todayWhy Systemd Timers Over Cron?
| Feature | Cron | Systemd Timer |
|---|---|---|
| Missed run catch-up | No | Yes (Persistent=true) |
| Random delay | No | Yes (RandomizedDelaySec) |
| Resource limits | No | Yes (CPUQuota, MemoryMax) |
| Dependencies | No | Yes (After=, Requires=) |
| Logging | Manual | Journald (built-in) |
| Status monitoring | crontab -l | systemctl list-timers |
| Overlap prevention | Manual (flock) | Built-in (oneshot) |
Get weekly IT automation tips
Docker, Ansible, Terraform, MLOps ā curated insights delivered to your inbox. No spam.
Subscribe Free āDebugging Cron
# Check if cron is running
systemctl status cron
# View cron logs
grep CRON /var/log/syslog
journalctl -u cron --since "1 hour ago"
# Test your script manually first
sudo -u www-data /opt/scripts/backup.sh
# Check cron mail
cat /var/mail/$(whoami)Security
# Restrict who can use cron
# /etc/cron.allow ā only listed users can use cron
# /etc/cron.deny ā listed users cannot use cron
echo "deploy" >> /etc/cron.allow
# Never run cron jobs as root unless necessary
# Use a dedicated service account
crontab -e -u backupuserWhat's Next?
Our SELinux for System Admins course covers securing cron jobs with SELinux policies. Ansible Automation in 30 Minutes teaches automating scheduled tasks across fleets. First lessons are free. -e ---
Ready to go deeper? Explore our hands-on DevOps courses ā practical labs covering Docker, Ansible, Terraform, and more.
Ready to learn by doing?
Stop reading tutorials ā start building. Expert video courses with hands-on labs in real sandboxed environments.
Related Articles
Bash Scripting for DevOps
Essential Bash scripting skills for DevOps engineers. Variables, conditionals, loops, functions, error handling, and practical automation scripts for daily.
Linux Networking for DevOps
Linux networking for DevOps. IP configuration, DNS, firewall rules, routing tables, packet capture with tcpdump, and troubleshooting.
Linux Process Management Guide
Manage Linux processes. ps, top, kill signals, background jobs, nice priorities, systemd services, and zombie process cleanup.
Linux Disk Management LVM Guide
Manage Linux disks with LVM. Physical volumes, volume groups, logical volumes, online resizing, snapshots, and migration strategies.
Linux File Permissions Explained
Master Linux file permissions. chmod, chown, umask, SUID, SGID, sticky bit, and ACLs with practical examples for system security.
Linux for Old Hardware in 2026
Revive old laptops and desktops with lightweight Linux distributions. Lubuntu, antiX, Puppy Linux, and more ā tested on low-spec hardware.
Explore topics
Browse more articles on the topics covered here.