systemd Timers vs cron: Modern Task Scheduling on Linux
A comparison of systemd timers and cron for scheduling tasks on Linux, with practical examples and migration guidance.
Introduction
For decades, cron has been the standard way to schedule tasks on Linux. But systemd timers offer a modern alternative with better logging, dependency management, and flexibility. While cron is simple and ubiquitous, systemd timers are becoming the preferred approach on modern Linux distributions. This post compares both approaches and explains when to use each.
Environment
The examples use systemd 252 on Debian 12, with both cron and systemd timers configured for comparison. The system runs several scheduled tasks including backups, log rotation, and health checks.
systemctl --version
# systemd 252 (252.19-1~deb12u1)
cron --version
# cron (Ubuntu/Debian 3.0pl1-128ubuntu3)Problem
You need to schedule a daily backup and a health check that runs every 5 minutes. With cron, you configure /etc/crontab or user crontabs. But cron has limitations:
- No built-in dependency management
- Limited logging (only syslog)
- No way to start a missed job
- Environment variables differ from the user's shell
- No resource limits on scheduled tasks
Analysis
cron characteristics:
- Simple syntax:
minute hour day month weekday command - Runs as the user specified in crontab
- Limited logging via syslog
- No dependency on other services
- Environment variables are minimal
- Widely supported and understood
systemd timer characteristics:
- Calendar-based and monotonic triggers
- Integration with systemd logging (journalctl)
- Dependency management (After=, Requires=)
- Resource limits via cgroups
- Missed runs can be caught up
- Each timer has a corresponding service unit
Solution
1. Basic cron configuration
# /etc/crontab
# m h dom mon dow user command
0 2 * * * root /opt/scripts/backup.sh
*/5 * * * * root /opt/scripts/healthcheck.shOr using user crontab:
crontab -e
0 2 * * * /opt/scripts/backup.sh2. Equivalent systemd timer
Create the service unit:
# /etc/systemd/system/backup.service
[Unit]
Description=Daily Backup
After=network-online.target
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
User=rootCreate the timer unit:
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily Backup Timer
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.targetEnable and start:
sudo systemctl enable backup.timer
sudo systemctl start backup.timer3. Health check timer (every 5 minutes)
# /etc/systemd/system/healthcheck.service
[Unit]
Description=System Health Check
[Service]
Type=oneshot
ExecStart=/opt/scripts/healthcheck.sh
# /etc/systemd/system/healthcheck.timer
[Unit]
Description=Health Check Timer
[Timer]
OnBootSec=1min
OnUnitActiveSec=5min
[Install]
WantedBy=timers.target4. Compare logging
# cron: Check syslog
grep CRON /var/log/syslog
# Jan 15 02:00:01 server CRON[12345]: (root) CMD (/opt/scripts/backup.sh)
# systemd timer: Use journalctl
journalctl -u backup.service
# Jan 15 02:00:01 server systemd[1]: Starting Daily Backup...
# Jan 15 02:00:05 server backup.sh[12346]: Starting backup...
# Jan 15 02:01:00 server systemd[1]: Daily Backup: Succeeded.5. Check timer status
systemctl list-timers
# NEXT LEFT LAST PASSED UNIT ACTIVATES
# Mon 2025-01-16 02:00:00 UTC 22h left Mon 2025-01-15 02:00:00 UTC 1d ago backup.timer backup.service
# Mon 2025-01-15 10:05:00 UTC 4min left Mon 2025-01-15 10:00:00 UTC 1min ago healthcheck.timer healthcheck.service6. Handle missed runs
# systemd timer with Persistent=true
# If the system was off during a scheduled run, it will run on boot
# cron does not have this behavior by default
# A missed cron job is simply skipped7. Set resource limits for scheduled tasks
# /etc/systemd/system/backup.service
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
MemoryMax=1G
CPUQuota=50%cron has no built-in resource limiting.
8. Use monotonic timers for relative schedules
# /etc/systemd/system/cleanup.timer
[Timer]
OnBootSec=15min
OnUnitActiveSec=1h
Unit=cleanup.serviceThis runs 15 minutes after boot and then every hour. cron cannot express this directly.
9. Calendar-based scheduling
# Run at 2:30 AM on weekdays
OnCalendar=Mon..Fri *-*-* 02:30:00
# Run on the first day of each month
OnCalendar=*-*-01 03:00:00
# Run every 6 hours
OnCalendar=*-*-* 00/6:00:0010. Migration checklist
# Before migrating from cron:
# 1. Document all existing cron jobs
crontab -l
sudo cat /etc/crontab
ls /etc/cron.d/
# 2. Create systemd service and timer units
# 3. Test with systemd-analyze calendar
systemd-analyze calendar "Mon..Fri *-*-* 02:30:00"
# *-*-* 02:30:00: Sun 2025-01-19 02:30:00 UTC
# *-*-* 02:30:00: Mon 2025-01-20 02:30:00 UTC
# 4. Enable and verify
sudo systemctl enable --now backup.timer
sudo journalctl -u backup.service
# 5. Remove old cron entries
sudo crontab -eLessons Learned
systemd timers are the modern approach to task scheduling on Linux. They offer better logging, dependency management, and resource control compared to cron. For new deployments, prefer systemd timers. For existing systems, migrate gradually, starting with less critical tasks. The Persistent=true option is particularly valuable for tasks that must not be missed. And always use journalctl to debug systemd timer issues.
This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.