Comparison

Cron vs systemd timers.

systemd timers are the modern Linux alternative to cron. They are more verbose to define but offer better logging, missed-run handling, dependency tracking, and resource management. This guide compares both and shows when to use each.

Quick comparison

Cronsystemd timers
Files per jobOne line in a crontabTwo files: .service + .timer
Schedule syntax5-field cronOnCalendar=Mon..Fri *-*-* 09:00:00
LoggingStderr to mailbox (often lost)journald, queryable with journalctl
Missed runsLost (unless anacron is installed)Run at next boot with Persistent=true
Resource limitsNone built-inFull cgroup limits (memory, CPU, I/O)
DependenciesNoneFull service dependency chains (After=, Requires=)
TimezoneSystem or CRON_TZPer-timer timezone in OnCalendar
Available sinceForeversystemd 209 (2014); systemd-everywhere by ~2017

systemd timer syntax

A systemd timer consists of two units: a .service defining what to run and a .timer defining when.

The service file

/etc/systemd/system/nightly-backup.service:

[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=backup
WorkingDirectory=/var/lib/backup

The timer file

/etc/systemd/system/nightly-backup.timer:

[Unit]
Description=Nightly backup timer

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

[Install]
WantedBy=timers.target

Activating the timer

sudo systemctl daemon-reload
sudo systemctl enable --now nightly-backup.timer
systemctl list-timers              # Verify it's loaded

OnCalendar syntax

More expressive than cron, with named days and clearer time format:

ExpressionMeaning
OnCalendar=*-*-* 09:00:00Every day at 9 AM
OnCalendar=Mon..Fri *-*-* 09:00:00Weekdays at 9 AM
OnCalendar=*-*-01 00:00:00First of every month at midnight
OnCalendar=Mon *-*-* 09:00:00Every Monday at 9 AM
OnCalendar=Mon *-*-* 09:00:00 America/New_YorkSame, in Eastern time
OnCalendar=*:0/15Every 15 minutes
OnUnitActiveSec=1hOne hour after the previous run finished

Where systemd timers win

1. Missed-run handling

If the system was off when the timer should have fired, Persistent=true ensures it runs at next boot. Cron silently skips missed runs unless you set up anacron separately.

2. Logging

Every run's output, exit code, and duration are captured by journald:

journalctl -u nightly-backup.service          # All runs
journalctl -u nightly-backup.service -f       # Tail live
systemctl status nightly-backup.timer         # When did it last run? Next?

3. Resource limits

You can constrain memory, CPU, and I/O via cgroups in the service file:

[Service]
ExecStart=/usr/local/bin/heavy-job.sh
MemoryMax=2G
CPUQuota=50%
IOWeight=10

This is impossible with classic cron without external tooling.

4. Dependency chains

A timer can require other services to be active first:

[Unit]
Description=Generate report after database backup
Requires=postgres-backup.service
After=postgres-backup.service

5. Timezone correctness

You can specify the timezone inline in OnCalendar, avoiding the trap of forgetting to set CRON_TZ.

6. Easier debugging

To run a job once on demand: systemctl start nightly-backup.service. You get the same logging, exit code, and resource accounting as a scheduled run. With cron, you'd have to manually invoke the script.

Where cron is still better

1. Verbose for simple jobs

One crontab line vs two systemd files. For a "run this script daily" job, cron is just faster to set up.

2. Universally understood

Every Linux/macOS sysadmin knows cron syntax. Many know systemd timers in theory but write them rarely.

3. Portable

Cron works on macOS, BSD, Alpine, busybox, container images without systemd. systemd timers are Linux-systemd only.

4. Per-user scheduling

Cron has crontab -e as an unprivileged operation. systemd has user-mode timers (--user) but they require additional setup.

Migrating from cron to systemd timers

Take an existing crontab entry like:

0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

It becomes a service file and a timer file:

/etc/systemd/system/backup.service:

[Unit]
Description=Daily backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
StandardOutput=append:/var/log/backup.log
StandardError=append:/var/log/backup.log

/etc/systemd/system/backup.timer:

[Unit]
Description=Daily backup at 2 AM

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

[Install]
WantedBy=timers.target

Enable and verify:

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

Recommendation

For modern Linux production servers: systemd timers. The logging, missed-run handling, and resource limits more than make up for the extra verbosity.

For containers, simple cron servers, and macOS dev environments: cron remains fine. There's no urgency to migrate working systems.

Related

Continue reading.