Running scripts manually is fine for development. In production, your scripts need to run automatically—every hour, every day, every week. Cron has been the standard for decades. Systemd timers are the modern replacement with better logging and dependency management. In this lesson, you'll learn both systems and when to use each.
1. Learning Objectives
By the end of this lesson, you will be able to:
- Schedule scripts with cron and crontab
- Understand cron syntax (minute, hour, day, month, weekday)
- Create systemd timer units
- Monitor and log scheduled jobs
- Handle job failures and notifications
- Choose between cron and systemd timers
2. Why This Matters
Real-world scenario: Your database needs nightly backups. Logs need rotating weekly. Cache needs clearing every hour. Doing these tasks manually is impossible. Cron and systemd timers automate repetitive tasks so you can focus on what matters.
3. Core Concepts
Cron Basics
# View your crontab
crontab -l
# Edit your crontab
crontab -e
# Remove your crontab
crontab -r
# Crontab syntax:
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12)
# │ │ │ │ ┌───────────── day of week (0 - 6) (Sunday=0 or 7)
# │ │ │ │ │
# * * * * * command_to_execute
# Examples:
# Run every minute
* * * * * /home/user/script.sh
# Run every hour at minute 0
0 * * * * /home/user/hourly_task.sh
# Run daily at 2:30 AM
30 2 * * * /home/user/daily_backup.sh
# Run weekly on Sunday at 3 AM
0 3 * * 0 /home/user/weekly_report.sh
# Run monthly on the 1st at midnight
0 0 1 * * /home/user/monthly_cleanup.sh
# Run every 15 minutes
*/15 * * * * /home/user/frequent_task.sh
# Run at 9 AM on weekdays
0 9 * * 1-5 /home/user/weekday_task.sh
# Run at 6 PM on weekends
0 18 * * 6,0 /home/user/weekend_task.sh
Cron syntax and examples
# Common cron patterns with real commands
# Backup database daily at 1 AM
0 1 * * * /usr/bin/mysqldump --all-databases > /backup/db_$(date +\%Y\%m\%d).sql
# Clear cache every hour
0 * * * * /usr/bin/find /tmp -type f -atime +1 -delete
# Check disk space and alert if low
*/30 * * * * /usr/bin/df -h / | /usr/bin/awk 'NR==2 {if ($5+0 > 90) system("echo 'Disk full' | mail -s Alert admin")}'
# Rotate logs daily
0 0 * * * /usr/sbin/logrotate /etc/logrotate.conf
# Update system weekly
0 2 * * 0 /usr/bin/apt update && /usr/bin/apt upgrade -y
# Check website uptime every 5 minutes
*/5 * * * * /usr/bin/curl -f https://example.com || /usr/bin/echo "Site down" | /usr/bin/mail -s "Alert" admin@example.com
# Sync files to backup server hourly
0 * * * * /usr/bin/rsync -avz /important/data/ backup@server:/backup/
Real-world cron examples
Cron Environment (Important!)
# Cron runs with a minimal environment (not your shell)
# Common issues: PATH not set, environment variables missing
# ALWAYS use full paths in cron
# Bad:
0 * * * * myscript.sh
# Good:
0 * * * * /home/user/scripts/myscript.sh
# Set PATH at top of crontab
PATH=/usr/local/bin:/usr/bin:/bin
# Set working directory
0 * * * * cd /home/user/scripts && ./myscript.sh
# Set environment variables
MAILTO=admin@example.com
SHELL=/bin/bash
HOME=/home/user
# Source your profile if needed
0 * * * * source /home/user/.bashrc && /home/user/scripts/myscript.sh
# Redirect output for debugging
0 * * * * /home/user/script.sh > /tmp/script.log 2>&1
# Disable email output (add >/dev/null 2>&1)
0 * * * * /home/user/quiet_script.sh >/dev/null 2>&1
Cron environment best practices
Systemd Timers (Modern Alternative)
# Systemd timers are defined in two files:
# 1. Service file (what to run)
# 2. Timer file (when to run it)
# Service file: /etc/systemd/system/backup.service
sudo cat > /etc/systemd/system/backup.service << 'EOF'
[Unit]
Description=Daily backup job
[Service]
Type=oneshot
ExecStart=/home/user/scripts/backup.sh
User=root
Group=root
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
# Timer file: /etc/systemd/system/backup.timer
sudo cat > /etc/systemd/system/backup.timer << 'EOF'
[Unit]
Description=Run backup daily at 2 AM
Requires=backup.service
[Timer]
OnCalendar=daily
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
EOF
# Enable and start timer
sudo systemctl daemon-reload
sudo systemctl enable backup.timer
sudo systemctl start backup.timer
# Check timer status
systemctl list-timers
systemctl status backup.timer
# Check service logs
journalctl -u backup.service
Creating systemd timers
# Timer OnCalendar expressions (more readable than cron)
# Every minute
OnCalendar=*-*-* *:*:00
# Every hour at minute 0
OnCalendar=hourly
OnCalendar=*-*-* *:00:00
# Daily at 2:30 AM
OnCalendar=daily
OnCalendar=*-*-* 02:30:00
# Weekly on Monday at 3 AM
OnCalendar=weekly
OnCalendar=Mon *-*-* 03:00:00
# Monthly on the 1st at midnight
OnCalendar=monthly
OnCalendar=*-*-01 00:00:00
# Every 15 minutes
OnCalendar=*:0/15
# Weekdays at 9 AM
OnCalendar=Mon..Fri *-*-* 09:00:00
# First Sunday of each month
OnCalendar=Sun *-*-01..07 00:00:00
# Randomized delay (avoid thundering herd)
[Timer]
OnCalendar=daily
RandomizedDelaySec=30min
# Persistent (run missed timers after boot)
[Timer]
OnCalendar=daily
Persistent=true
# Accuracy (power saving)
[Timer]
OnCalendar=daily
AccuracySec=1h
Systemd timer calendar expressions
Comparing Cron vs Systemd Timers
4. Complete Project: Automated Backup System
#!/bin/bash
# automated_backup.sh - Complete backup with scheduling
# Backup script (save as /usr/local/bin/backup.sh)
BACKUP_DIR="/backup"
RETENTION_DAYS=30
LOG_FILE="/var/log/backup.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
log "Starting backup..."
# Create backup
BACKUP_FILE="$BACKUP_DIR/backup_$(date +%Y%m%d_%H%M%S).tar.gz"
tar -czf "$BACKUP_FILE" /home/user/data 2>/dev/null
if [ $? -eq 0 ]; then
SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
log "Backup successful: $BACKUP_FILE ($SIZE)"
# Remove old backups
find "$BACKUP_DIR" -name "backup_*.tar.gz" -mtime +$RETENTION_DAYS -delete
log "Removed backups older than $RETENTION_DAYS days"
else
log "ERROR: Backup failed!"
exit 1
fi
log "Backup completed"
# Cron setup
# 0 2 * * * /usr/local/bin/backup.sh
# Systemd timer setup
# /etc/systemd/system/backup.service
# /etc/systemd/system/backup.timer
Complete automated backup system
5. Monitoring Scheduled Jobs
# Check cron logs (on Debian/Ubuntu)
grep CRON /var/log/syslog
# Check cron logs (on RHEL/CentOS)
grep CRON /var/log/cron
# Systemd timer logs
journalctl -u mytimer.timer
journalctl -u myservice.service
# List all systemd timers
systemctl list-timers --all
# Show next run times
systemctl list-timers | grep mytimer
# Get detailed timer info
systemctl show mytimer.timer
# Email alerts from cron
# Set MAILTO in crontab
MAILTO=admin@example.com
0 2 * * * /usr/local/bin/backup.sh
# Health check script
#!/bin/bash
# check_jobs.sh - Verify scheduled jobs ran
EXPECTED_TIME=$(date -d "yesterday 02:00:00" +%s)
ACTUAL_TIME=$(stat -c %Y /backup/latest_backup.tar.gz)
if [ $ACTUAL_TIME -lt $EXPECTED_TIME ]; then
echo "Backup did not run last night!"
exit 1
fi
Monitoring and alerting
6. Common Errors & Solutions
7. Summary Checklist
8. Next Steps
Next category: Git & GitHub - Version Control
You'll learn to:
- Track changes with Git
- Collaborate with GitHub
- Manage branches and merges
- Implement CI/CD workflows
Comments (0)
This is exactly what I needed! The initContainer approach solved our migration issues completely. Thanks for the detailed guide!
ReplyLeave a Comment