
In this story
Systemd timers are a cron replacement that offer richer integration with the unit system and missed-run handling. While cron remains the simpler legacy scheduler, systemd's integration and unit-based management make it the modern choice for Linux admins.
systemd as the Replacement Model
Systemd timers use a .timer unit to schedule and track a matching .service unit. This design makes them a first-class part of the systemd service manager. Compared to the standalone crontab files, systemd timers are just another kind of unit in the systemd hierarchy.
All cron jobs can be migrated to timers, per SUSE's documentation. The systemd suite provides calendar time events for realtime scheduling, and monotonic timers that count from an event such as boot (OnBootSec=) or the last run (OnUnitActiveSec=), as documented by the ArchWiki. This unit-based hierarchy is what makes systemd's timers set themselves apart from cron.
Syntax and Conversion
Systemd timers use OnCalendar= for realtime scheduling that follows a calendar format, rather than the traditional five-field cron time field. The SUSE migration guide maps the cron @reboot keyword to the OnBootSec=1s timer field. It also notes that mailto cron variables are not directly migrateable to the timer's service file.
A few common translations:
0 3 * * * -> OnCalendar=*-*-* 03:00:00
*/15 * * * * -> OnCalendar=*:0/15
0 9 * * 1 -> OnCalendar=Mon *-*-* 09:00:00
@daily -> OnCalendar=daily
Test an expression before using it with systemd-analyze calendar "Mon *-*-* 09:00:00", which prints when it will next fire.
Missed Runs and Boot-Time Catch-up
With Persistent=true in the [Timer] section, systemd records when a timer last fired. If the machine was off at the scheduled time, the job runs once soon after the next boot. Plain cron simply skips a run it missed; anacron covers daily, weekly and monthly jobs on machines that are not always on, but not finer schedules.
The systemd-cron project goes further: it reads existing crontab files and generates matching timer and service units automatically, which is a quick way to try timers without rewriting jobs by hand.
Logging and Troubleshooting
Each timer starts a service, so every run is logged in the journal. systemctl list-timers shows when each timer last ran and when it will run next, journalctl -u backup.service shows a job's output, and systemctl start backup.service runs the job by hand without waiting for the schedule.
A minimal pair looks like this:
# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
# /etc/systemd/system/backup.timer
[Unit]
Description=Run the backup every night
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
Enable it with systemctl enable --now backup.timer.
One thing cron does that timers do not: cron mails a job's output to the user by default. Timers send output to the journal instead, so if you relied on those emails, add an OnFailure= unit that sends an alert.
What timers add beyond cron
- Missed runs caught up after boot with Persistent=true.
- Logging to the journal, with status in systemctl.
- Random delays (RandomizedDelaySec=) to spread load across machines.
- Dependencies, resource limits and sandboxing from the service unit.
Recommendation
Cron is still the quickest way to schedule a one-line job, and it works the same on almost every Unix-like system. Timers take two files instead of one line, but give you catch-up after downtime, proper logging and the rest of systemd's controls.
On a modern systemd distribution, timers are the better choice for jobs that matter, such as backups, certificate renewals and cleanup. Keep cron for quick personal jobs or systems without systemd. Running both side by side is fine, as long as each job is scheduled in only one of them.


