Stage 2 · Tools
Process & System Management
cron & systemd Timers
Schedule jobs with cron syntax and modern systemd timer units.
cron Basics
cron is the classic Unix job scheduler. It runs commands at specified times and intervals. Every user can have their own crontab, and the system has a global crontab for administrative tasks.
# Edit your crontab
crontab -e
# List your cron jobs
crontab -l
# Remove all cron jobs
crontab -r
# Edit system crontab
sudo vim /etc/crontab
# View cron logs
grep CRON /var/log/syslog # Linux
grep cron /var/log/system.log # macOScrontab -e opens your crontab in an editor. Each line is a cron job. The system crontab (/etc/crontab) includes a user field — user crontabs do not.
cron Syntax
cron expressions have five time fields followed by the command. Each field can be a specific value, a range, a list, or an interval.
| Field | Values | Example |
|---|---|---|
| Minute | 0-59 | */5 (every 5 minutes) |
| Hour | 0-23 | 9-17 (9am to 5pm) |
| Day of month | 1-31 | 1,15 (1st and 15th) |
| Month | 1-12 | 1,4,7,10 (quarterly) |
| Day of week | 0-7 (0=7=Sun) | 1-5 (weekdays) |
# Every 5 minutes
*/5 * * * * /path/to/script.sh
# Every day at 2:30 AM
30 2 * * * /path/to/backup.sh
# Every weekday at 9 AM
0 9 * * 1-5 /path/to/report.sh
# First of every month at midnight
0 0 1 * * /path/to/monthly.sh
# Every 15 minutes between 8am-6pm on weekdays
*/15 8-18 * * 1-5 /path/to/check.sh
# Every Sunday at 3 AM
0 3 * * 0 /path/to/weekly.shThe * means 'every'. */5 means 'every 5 units'. 1-5 means '1 through 5'. 1,3,5 means '1, 3, and 5'. These can be combined for complex schedules.
cron runs with a minimal environment — no PATH, no aliases. Always use full paths to commands and scripts: /usr/bin/python3 /opt/scripts/task.py not python3 task.py.
cron Best Practices
#!/usr/bin/env bash
# /opt/scripts/backup.sh
set -euo pipefail
LOG_FILE="/var/log/backup.log"
LOCK_FILE="/tmp/backup.lock"
# Prevent concurrent runs
if [[ -f "$LOCK_FILE" ]]; then
echo "$(date): Backup already running, skipping" >> "$LOG_FILE"
exit 0
fi
trap 'rm -f "$LOCK_FILE"' EXIT
touch "$LOCK_FILE"
echo "$(date): Starting backup" >> "$LOG_FILE"
# ... backup logic ...
echo "$(date): Backup complete" >> "$LOG_FILE"Cron jobs should: 1) Use a lock file to prevent concurrent runs. 2) Log output for debugging. 3) Handle errors. 4) Set PATH explicitly if needed. 5) Be idempotent.
systemd Timers
systemd timers are the modern replacement for cron. They offer better logging, dependency management, calendar-based scheduling, and persistent timers that catch up missed runs.
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily backup timer
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=600
[Install]
WantedBy=timers.targetOnCalendar uses calendar events. Persistent=true means if the machine was off at 2:30 AM, the job runs when it boots. RandomizedDelaySec adds jitter to prevent thundering herd.
# /etc/systemd/system/backup.service
[Unit]
Description=Daily backup
After=network-online.target
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.targetThe service unit defines what to run. Type=oneshot means it runs once and exits. The timer triggers the service. journal captures output for systemd's logging.
# Enable and start the timer
sudo systemctl enable --now backup.timer
# Check timer status
systemctl status backup.timer
# List all active timers
systemctl list-timers --all
# Manually trigger the service
sudo systemctl start backup.service
# View logs
journalctl -u backup.servicesystemd timers replace cron with better features: dependency management, logging integration, missed-run recovery, and resource limits via cgroups.
Timer Examples
# Every 5 minutes
OnCalendar=*-*-* *:00/05:00
# Every hour
OnCalendar=hourly
# Daily at midnight
OnCalendar=daily
# Every Monday at 9 AM
OnCalendar=Mon *-*-* 09:00:00
# First of every month at 3 AM
OnCalendar=*-*-01 03:00:00
# Every 15 minutes during business hours
OnCalendar=Mon..Fri *-*-* 08:00/15:00..17:00
# Boot and then every hour
OnBootSec=5min
OnUnitActiveSec=1hsystemd timer expressions are more flexible than cron. You can use day-of-week ranges, specific dates, and boot-relative timers. See systemd.time(7) for the full specification.
Scheduling Patterns
These patterns cover common scheduling scenarios in production environments.
# cron: Rotate logs daily at midnight
0 0 * * * /usr/sbin/logrotate /etc/logrotate.conf
# cron: Check disk space every hour
0 * * * * /opt/scripts/check_disk.sh >> /var/log/disk_check.log 2>&1
# systemd: Deploy every Tuesday at 3 AM
OnCalendar=Tue *-*-* 03:00:00
# systemd: Health check every 30 seconds
OnBootSec=30s
OnUnitActiveSec=30s
# Overlapping jobs: Use a lock file
if ! mkdir /tmp/backup.lock 2>/dev/null; then
echo "Backup already running"
exit 0
fi
trap 'rmdir /tmp/backup.lock' EXITChoose cron for simple schedules and systemd timers for complex ones. Always handle job overlap with lock files. Log output for debugging missed runs.
Use https://crontab.guru/ to validate cron expressions. A typo in a cron expression can run a job every minute instead of every day. Always test with a non-destructive command first.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.