architecture2024-12-15·12 min·240/348

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.sh

Or using user crontab:

crontab -e
0 2 * * * /opt/scripts/backup.sh

2. 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=root

Create 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.target

Enable and start:

sudo systemctl enable backup.timer
sudo systemctl start backup.timer

3. 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.target

4. 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.service

6. 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 skipped

7. 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.service

This 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:00

10. 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 -e

Lessons 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.