systemd Timers vs Cron: Which Should You Use?
Introduction
For decades, cron was the undisputed champion of task scheduling on Unix-like systems. Then systemd arrived with its timer units, offering a modern alternative that integrates deeply with the systemd ecosystem.
This guide compares both approaches across key dimensions, helps you choose the right tool, and provides a practical migration path from cron to systemd timers.
Feature Comparison
| Feature | Cron | systemd Timers |
|---|---|---|
| Persistence | Missed jobs are lost | Can catch up on missed jobs (persistent timers) |
| Logging | Requires manual redirection | Integrated with journalctl |
| Dependencies | None (runs independently) | Can depend on other units, network, filesystem mounts |
| Random Delay | Manual scripting required | Built-in RandomizedDelaySec |
| Accuracy | Minute-level precision | Sub-second precision possible |
| User Jobs | Per-user crontabs | User timers supported (systemctl --user) |
| Monitoring | External tools needed | systemctl status and systemctl list-timers |
| Configuration | Single crontab file | Separate timer and service unit files |
| Boot-time Jobs | @reboot directive | OnBootSec directive |
| Resource Limits | Manual ulimit/niceness | Built-in cgroup integration |
When to Use Cron
Cron remains the better choice in these scenarios:
1. Simple, Frequent Tasks
If you need a quick one-liner that runs every minute or hour, cron's syntax is faster to write:
*/5 * * * * /path/to/simple-script.sh
2. Portability Across Systems
Cron works on virtually every Unix-like system—from embedded Linux to macOS to BSD. systemd timers only work on systemd-based systems.
3. Quick User-level Scheduling
For personal automation scripts, crontab -e is faster than creating unit files:
crontab -e
# Add: 0 9 * * 1-5 /home/user/morning-reminder.sh
4. Legacy System Maintenance
If you're maintaining older systems or writing scripts that must work on non-systemd distributions, cron is the safe choice.
When to Use systemd Timers
systemd timers excel in these situations:
1. Tasks That Must Survive System Sleep/Hibernation
systemd timers can be configured as persistent, meaning if the system was off when the job should have run, it will execute when the system wakes up:
[Timer]
OnCalendar=daily
Persistent=true
2. Jobs Requiring Dependencies
Need to ensure the network is up, or a filesystem is mounted? systemd handles this natively:
[Unit]
After=network-online.target
Wants=network-online.target
[Timer]
OnCalendar=hourly
3. Integrated Logging and Monitoring
systemd timers automatically log to the journal, making debugging easier:
# View logs for a specific timer
journalctl -u my-service.timer -f
# View logs for the associated service
journalctl -u my-service.service
4. Randomized Execution (Prevent Thundering Herd)
When you have many servers running the same job, use randomized delays to spread the load:
[Timer]
OnCalendar=hourly
RandomizedDelaySec=1800 # Up to 30 minutes random delay
5. Resource-constrained Environments
Limit CPU, memory, or I/O for the scheduled task:
[Service]
ExecStart=/path/to/resource-heavy-script.sh
MemoryMax=512M
CPUQuota=50%
IOWeight=100
Migration Guide: From Cron to systemd Timers
Step 1: Understand the Mapping
| Cron Expression | systemd Timer Equivalent |
|---|---|
0 * * * * | OnCalendar=hourly |
0 0 * * * | OnCalendar=daily or OnCalendar=*-*-* 00:00:00 |
0 0 * * 0 | OnCalendar=weekly or OnCalendar=Sun *-*-* 00:00:00 |
*/15 * * * * | OnCalendar=*:0/15 |
0 9 15 * * | OnCalendar=*-*-15 09:00:00 |
Step 2: Create the Service Unit
For a cron job like:
0 2 * * * /home/user/backup.sh --full
Create /etc/systemd/system/backup.service:
[Unit]
Description=Full system backup
Documentation=man:backup(1)
[Service]
Type=oneshot
ExecStart=/home/user/backup.sh --full
User=backup-user
Group=backup-group
# Optional: Resource limits
IOSchedulingClass=best-effort
IOSchedulingPriority=7
Step 3: Create the Timer Unit
Create /etc/systemd/system/backup.timer:
[Unit]
Description=Run backup daily at 2 AM
Documentation=man:backup(1)
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
RandomizedDelaySec=300 # Up to 5 minutes random delay
[Install]
WantedBy=timers.target
Step 4: Enable and Start
# Reload systemd to recognize new units
sudo systemctl daemon-reload
# Enable the timer (starts on boot)
sudo systemctl enable backup.timer
# Start the timer (starts now)
sudo systemctl start backup.timer
# Verify it's scheduled
systemctl list-timers backup.timer
Step 5: Monitor and Debug
# Check timer status
systemctl status backup.timer
# List all active timers
systemctl list-timers --all
# View next scheduled run
systemctl list-timers backup.timer --no-pager
# Check service logs after it runs
journalctl -u backup.service -n 50
Advanced systemd Timer Features
Calendar Expressions
systemd supports rich calendar expressions:
# Every 15 minutes
OnCalendar=*:0/15
# First day of every month at 6 AM
OnCalendar=*-*-01 06:00:00
# Every Monday and Friday at 9 AM
OnCalendar=Mon,Fri *-*-* 09:00:00
# Last day of the month
OnCalendar=*-*~01 23:59:00
Multiple Trigger Conditions
A timer can have multiple triggers:
[Timer]
OnBootSec=10min # 10 minutes after boot
OnCalendar=daily # Then daily
OnUnitActiveSec=1h # Also every hour after service activates
Wake System from Suspend
[Timer]
OnCalendar=08:00
WakeSystem=true # Wake the system from suspend if supported
Decision Flowchart
Is the task simple and portable?
├─ Yes → Use Cron
└─ No → Does it need to survive system sleep?
├─ Yes → Use systemd Timer
└─ No → Does it need dependencies (network, mounts)?
├─ Yes → Use systemd Timer
└─ No → Do you want integrated logging?
├─ Yes → Use systemd Timer
└─ No → Use Cron
Common Pitfalls
Cron Pitfalls
- Silent failures: No built-in logging; must redirect output manually
- Environment differences: Cron runs with minimal PATH
- Missed jobs: If system is off, the job doesn't run
systemd Timer Pitfalls
- More files to manage: Need both
.serviceand.timerfiles - Steeper learning curve: Unit file syntax is more complex than crontab
- systemd dependency: Only works on systemd-based systems
Quick Reference
Cron Cheat Sheet
# Edit user crontab
crontab -e
# List cron jobs
crontab -l
# System-wide cron
cat /etc/crontab
ls /etc/cron.d/
systemd Timer Cheat Sheet
# List active timers
systemctl list-timers
# Start/stop a timer
systemctl start backup.timer
systemctl stop backup.timer
# Enable/disable (boot persistence)
systemctl enable backup.timer
systemctl disable backup.timer
# View logs
journalctl -u backup.service
journalctl -u backup.timer
Related Tools
- Cron Parser - Decode and validate cron expressions