systemd Timers vs Cron: Which Should You Use?

May 26, 202610 min read

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

FeatureCronsystemd Timers
PersistenceMissed jobs are lostCan catch up on missed jobs (persistent timers)
LoggingRequires manual redirectionIntegrated with journalctl
DependenciesNone (runs independently)Can depend on other units, network, filesystem mounts
Random DelayManual scripting requiredBuilt-in RandomizedDelaySec
AccuracyMinute-level precisionSub-second precision possible
User JobsPer-user crontabsUser timers supported (systemctl --user)
MonitoringExternal tools neededsystemctl status and systemctl list-timers
ConfigurationSingle crontab fileSeparate timer and service unit files
Boot-time Jobs@reboot directiveOnBootSec directive
Resource LimitsManual ulimit/nicenessBuilt-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 Expressionsystemd Timer Equivalent
0 * * * *OnCalendar=hourly
0 0 * * *OnCalendar=daily or OnCalendar=*-*-* 00:00:00
0 0 * * 0OnCalendar=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 .service and .timer files
  • 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