systemd Timer vs cron: Choosing the Right Scheduler

systemd Timer vs cron: Choosing the Right Scheduler

Which Should You Use?

Default to systemd timer for new workloads. Logs integrate with journalctl, dependencies between services can be enforced, and missed runs are caught on the next boot. Stick with cron when maintaining existing jobs or when a single-line schedule is all you need.

Quick Decision Table

Scenario Recommended
New periodic job systemd timer
Centralized logs via journalctl systemd timer
Depends on another service systemd timer
Boot-relative trigger needed systemd timer
Maintaining existing cron jobs Keep cron
Simple one-liner on a schedule cron

What Is cron?

cron is the traditional UNIX job scheduler. A single line in crontab defines when and what to run, using five fields: minute, hour, day-of-month, month, and day-of-week.

crontab Syntax

min hour dom month dow command
# Every day at 3:00 AM
0 3 * * * /home/user/scripts/backup.sh

# Every 5 minutes
*/5 * * * * /usr/local/bin/collect-logs.sh

# Every Monday at 9:00 AM
0 9 * * 1 /usr/local/bin/send-report.sh

crontab Commands

# Open the editor for the current user
crontab -e

# List current jobs
crontab -l

# Remove all jobs (use with caution)
crontab -r

cron mails output to the local user by default. In environments without a mail daemon, output accumulates in /var/spool/mail. Add MAILTO="" at the top of your crontab to suppress this.

What Is systemd timer?

A systemd timer consists of two unit files: a .timer file (the schedule) and a .service file (the command to run). Both are managed by systemd, so logs, dependencies, and status checks all go through the same interface.

Minimal Example: Run Every 5 Minutes

/etc/systemd/system/collect-logs.service

[Unit]
Description=Collect system logs

[Service]
Type=oneshot
ExecStart=/usr/local/bin/collect-logs.sh

/etc/systemd/system/collect-logs.timer

[Unit]
Description=Run collect-logs every 5 minutes

[Timer]
OnCalendar=*:0/5
Persistent=true

[Install]
WantedBy=timers.target

Enabling and Monitoring a Timer

# Reload unit files after any change
sudo systemctl daemon-reload

# Enable and start the timer immediately
sudo systemctl enable --now collect-logs.timer

# List all active timers with next run times
systemctl list-timers

# Check execution logs
journalctl -u collect-logs.service

Why Is systemd Timer Better?

The two key advantages over cron are centralized logging and fine-grained control.

Logs Go to journalctl

# Last 50 log entries
journalctl -u collect-logs.service -n 50

# Logs since today only
journalctl -u collect-logs.service --since today

# Errors only
journalctl -u collect-logs.service -p err

cron output goes to mail or a separate file. systemd timer output lands in the journal alongside all other system events, making incident investigation much faster.

Service Dependencies

[Unit]
Description=Database backup
Requires=postgresql.service
After=postgresql.service

The job only runs if PostgreSQL is active. cron has no equivalent mechanism — you would need to check inside the script instead.

Boot-Relative Triggers

[Timer]
# 10 minutes after boot
OnBootSec=10min
# Then every hour
OnUnitActiveSec=1h

systemd timer supports both calendar-based (OnCalendar) and relative (OnBootSec/OnUnitActiveSec) schedules. cron supports neither.

Catching Missed Runs with Persistent=true

[Timer]
OnCalendar=daily
Persistent=true

If the machine is off at the scheduled time, the job runs on the next boot instead of being skipped silently. cron does not do this without anacron.

How Do You Write OnCalendar?

OnCalendar uses systemd's own timestamp format. Always validate with systemd-analyze calendar before enabling a new timer.

# Validate a schedule and show next trigger times
systemd-analyze calendar "Mon *-*-* 03:00:00"

# Common patterns
OnCalendar=daily          # 00:00 every day
OnCalendar=hourly         # 00:00 every hour
OnCalendar=weekly         # Mon 00:00 every week
OnCalendar=*:0/5          # every 5 minutes
OnCalendar=Mon 03:00      # Mon 03:00 every week
OnCalendar=*-*-* 03:00:00 # every day at 03:00 (same as above)

Run systemd-analyze calendar before enabling any new timer. It prints the next two scheduled times, catching typos before they cause silent failures.

How Do You Migrate from cron to systemd Timer?

Step 1: Review Existing Jobs

crontab -l
# 0 3 * * * /home/user/scripts/backup.sh

Step 2: Create the Service File

sudo nano /etc/systemd/system/backup.service
[Unit]
Description=Daily backup

[Service]
Type=oneshot
User=user
ExecStart=/home/user/scripts/backup.sh

Step 3: Create the Timer File

sudo nano /etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily at 3:00 AM

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target

Step 4: Enable and Verify

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer

# Confirm the timer is registered
systemctl list-timers --all | grep backup

# Check logs after the first run
journalctl -u backup.service --since today

Step 5: Remove the cron Job

crontab -e
# Delete or comment out the migrated line

Confirm the timer is working via systemctl list-timers and journalctl -u backup.service before removing the original cron job. Leaving both active causes double execution.

Comparison Summary

Feature cron systemd timer
Setup complexity One line Two unit files
Logging Mail or file journalctl
Service dependencies No Yes
Boot-relative trigger No Yes (OnBootSec)
Catch missed runs No (needs anacron) Yes (Persistent=true)
User-level jobs crontab per user User service units
Inspection command crontab -l systemctl list-timers

Next Reading