systemd Timers vs Cron
systemd timers are the built-in scheduling mechanism on nearly every modern Linux distribution that uses systemd as its init system, offering dependency management, structured logging via journald, and a more expressive calendar syntax than cron's 5 fields — at the cost of needing two unit files instead of one crontab line.
Syntax Notes
Timers use OnCalendar= expressions, a different (and more expressive) syntax than cron: DayOfWeek Year-Month-Day Hour:Minute:Second, with wildcards (*), ranges, and steps (/) similar in spirit to cron but not directly interchangeable — for example '*-*-* 02:00:00' for daily at 2 AM, or 'Mon..Fri 09:00' for weekdays at 9 AM.
Examples
[Unit]
Description=Run backup script
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh[Unit]
Description=Run backup daily
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.targetsudo systemctl enable --now mybackup.timerGotchas
- A timer file alone does nothing — it must reference a matching .service file with Type=oneshot, and both files need matching base names (mybackup.timer + mybackup.service) or an explicit Unit= directive pointing to the service.
- OnCalendar syntax looks similar to cron at a glance but isn't compatible with it — porting a crontab entry means translating the expression, not copying it verbatim. Use `systemd-analyze calendar 'EXPRESSION'` to preview and validate before deploying.
- Without Persistent=true in the [Timer] section, a missed run (machine was off at the scheduled time) is simply skipped rather than caught up on next boot — the closest systemd equivalent to anacron's catch-up behavior.
Best Practices
- Validate any OnCalendar expression with `systemd-analyze calendar 'your-expression' --iterations=5` before enabling the timer — it prints the next several run times so a typo shows up before it causes a missed job.
- Use Persistent=true for anything that should catch up after downtime (backups, cleanup jobs) — it's the direct equivalent of cron+anacron's missed-job behavior, and it's off by default.
- Check timer status and history with `systemctl list-timers` and `journalctl -u mybackup.service` — systemd's built-in logging is a genuine advantage over plain cron, which requires the job itself to handle its own logging.
Frequently Asked Questions
- Should I switch from cron to systemd timers?
- If cron is already meeting your needs, there's little reason to switch. Timers are worth it specifically when you need dependency ordering (run after another service starts), built-in catch-up for missed runs, or centralized logging via journald without setting any of that up yourself.
- Can I just paste my crontab entry's schedule into OnCalendar?
- No — the syntaxes aren't compatible. A 5-field cron expression needs to be translated into systemd's OnCalendar format, which uses a different field order and wildcard style, not copied as-is.
- How do I see when a systemd timer will next run?
- Run `systemctl list-timers` to see every active timer's next scheduled run, or `systemd-analyze calendar 'OnCalendar-expression'` to preview run times for an expression before deploying it.
Related Tools
Platform Guides
Cron syntax for Linux, Kubernetes, AWS, and more
GitLab CI Scheduled Pipelines
How to schedule GitLab CI/CD pipelines with cron syntax: the Schedules UI, timezone handling, rules:if conditions, and common gotchas.
Celery Beat Schedule Guide
Schedule periodic tasks in Celery with Beat: the crontab() schedule helper, beat_schedule dict syntax, common pitfalls, and Django integration.