Cron vs systemd Timers: Choosing a Linux Job Scheduler

Cron and systemd timers both run recurring work on Linux, but they differ in how much they leave to the administrator. Cron is a one-line schedule that is portable and familiar. A systemd timer is a pair of unit files that adds journal logging, catch-up after downtime, dependency ordering and resource limits. The better choice depends on the environment in which the job runs more than on any abstract ranking of the tools, and the sections below set out the criteria that decide it.
TL;DR
- Cron suits simple, portable, set-and-forget scripts; systemd timers suit production servers where observability, failure reporting and resource constraints matter.
- Timers can run a missed job after boot through
Persistent=true, and their output and exit status are queryable withjournalctl. Cron has neither behavior natively. - Timers have no built-in equivalent of
MAILTO, so failure notification must be configured explicitly, usually withOnFailure=. - Both tools can coexist on one host. Migrate the jobs whose failures are costly, and leave trivial ones alone.
Table of Contents
- What each tool actually does
- Side-by-side comparison
- Logging and failure visibility
- Missed runs, daylight saving time and the wall clock
- Dependencies, ordering and resource control
- Spreading load: AccuracySec versus RandomizedDelaySec
- Security and access considerations
- A minimal migration example
- When cron is still the right answer
- When systemd timers are the better answer
- The systemd controversy and non-systemd systems
- Our perspective
- Support for scheduling and automation work
- FAQ
- Sources
What each tool actually does
Cron is a daemon that reads crontab files and starts commands at times described by a five-field expression: minute, hour, day of month, month and day of week. The crontab(5) manual page describes each file as instructions for the cron daemon in the form “run this command at this time on this date”, and each user may maintain a separate table. The model is deliberately minimal. Cron runs the command with a sparse environment (typically only PATH and SHELL are set), and any output is conventionally mailed to the job’s owner.
A systemd timer is a unit file whose name ends in .timer and which controls a matching .service unit. According to the ArchWiki entry on systemd timers, timers may be used as an alternative to cron and include built-in support for calendar time events as well as monotonic time events, which fire a set interval after a reference point such as boot. The timer holds the schedule, while the service holds the command and every property that systemd can apply to a service: user, working directory, environment, sandboxing and resource controls.
This separation of schedule from execution is the central design difference. Cron treats a job as a command line; systemd treats it as a managed service that happens to be started on a clock.
Side-by-side comparison
The table summarizes the differences that most often decide the question in practice. Items about directives are drawn from the ArchWiki and a LinuxToday comparison.
| Criterion | Cron | systemd timers |
|---|---|---|
| Configuration | One line in a crontab | Two unit files (.timer and .service) |
| Schedule syntax | Five fields | OnCalendar= expressions plus monotonic options such as OnBootSec= |
| Missed runs while powered off | Skipped | Run on next boot with Persistent=true |
| Logging | Mail, redirected files or syslog, depending on setup | Journal, queried with journalctl -u |
| Failure notification | MAILTO built in |
Manual, via OnFailure= |
| Dependencies | Not expressed | After=, Requires= and related directives |
| Resource limits | None natively | CPUQuota=, MemoryMax= and other cgroup controls |
| Load spreading | Hand-rolled sleep |
RandomizedDelaySec= |
| Portability | Nearly any Unix-like system | Linux distributions running systemd |
| Setup effort | Minimal | Moderate |
Logging and failure visibility
The most frequently cited operational benefit of timers is debuggability. When a cron job fails, its output is mailed to a local mailbox that nobody reads, appended to a file the author chose, or discarded. Establishing what happened often requires reconstructing the environment by hand. Under systemd, the service’s standard output, standard error and exit status are recorded by the journal, so a single journalctl -u backup.service produces the history of every run, and systemctl status reports the last result. The ArchWiki identifies this standardization of logs and exit statuses as an immediate long-term gain of migrating, even though initial setup is more involved.
The converse concerns notification. Cron’s mailing behavior, whatever its drawbacks, is plug-and-play: a MAILTO line is sufficient. A timer-driven service has no equivalent, and the ArchWiki notes that notification must be assembled with OnFailure=, which launches a separate unit (one that sends an email, posts to a chat webhook or pages an on-call rotation) when the service fails. That is a modest amount of configuration, but it is additional maintenance and it is easy to forget. A timer migrated without it is quieter than the cron job it replaced.

Missed runs, daylight saving time and the wall clock
Cron evaluates its table against the local clock every minute, which has two consequences. First, a job scheduled for a moment when the machine is off simply does not run. Second, local time transitions can distort schedules. An analysis of cron and daylight saving behavior recommends running servers and cron in UTC, or pinning jobs with CRON_TZ, and avoiding fixed local times that fall in the transition window. Jobs set for the hour that is skipped in spring or repeated in autumn may be missed or run twice depending on the implementation.
Systemd timers address the first problem directly. A calendar timer with Persistent=true stores the time it last fired on disk, and if one or more activations were missed while the system was down, it triggers the service immediately at the next start. This matters for laptops, development workstations and virtual machines that are not always on, and for any server that might reboot across a nightly window. Timers also offer monotonic triggers, such as a fixed interval after boot or after the previous activation, which are unaffected by changes to the wall clock. For work such as “run 15 minutes after startup, then every hour after the last completion”, monotonic triggers express the intent more accurately than any crontab line.
Timers do not make time-zone reasoning disappear. A calendar expression is still interpreted in a zone, and the sound practice, for both tools, is to keep production hosts in UTC unless a business requirement says otherwise.

Dependencies, ordering and resource control
Because a timer starts an ordinary service unit, the service can declare ordering and requirements. A job that must follow network availability, a mounted volume or a database unit can state After= and Requires= relations, and systemd will honor them. Cron has no such concept, and administrators typically substitute sleep calls or defensive checks inside scripts, which are brittle.
Resource controls follow the same logic. A service can be limited with CPUQuota= and MemoryMax=, which allows a heavy report or backup to share a host with latency-sensitive workloads without displacing them. The same unit can carry sandboxing directives; our article on systemd hardening for services examines how to restrict filesystem access, capabilities and system calls for exactly this kind of unattended job. A cron-launched script inherits the privileges of its user and is constrained only by what that user can do.
Spreading load: AccuracySec versus RandomizedDelaySec
One point of frequent confusion deserves precision. AccuracySec= defines a window within which systemd may fire a timer so that it can coalesce several timers and wake the CPU less often. It is an energy and scheduling optimization, not a randomization feature. Jobs within the same window may still start together, and if each is resource-intensive the host may experience a load spike. The freedesktop.org feature request that led to randomized delays states the motivation plainly: delaying timers randomly within an interval helps spread out I/O-intensive jobs. RandomizedDelaySec= is the directive that supplies that behavior.
The practical case is a fleet. If two hundred virtual machines each run a package metadata refresh at 03:00, the upstream mirror or shared storage receives two hundred simultaneous requests. A randomized delay of a few minutes in the timer converts that spike into a gentle slope, and no script needs to be edited.
Security and access considerations
Cron access control has a subtlety that is often misunderstood. The cron.allow and cron.deny files govern who may use the crontab command to edit tables. According to the crontab(1) manual page, tables are files under /var/spool/ that are not intended to be edited directly, and the research we reviewed notes that restricting a user’s access to the command does not terminate that user’s existing scheduled tasks. An administrator who revokes access during an offboarding should therefore also inspect and remove the existing table, not rely on the deny file alone.
Systemd units are inventoried differently. Because they are discoverable through systemctl list-timers and live in known directories, auditing every scheduled job on a host is a single command, whereas cron entries may be dispersed across per-user tables, /etc/cron.d, and the periodic directories. For organizations that review scheduled execution as part of a hardening baseline, the consolidated inventory is a practical advantage. Our system hardening checklist treats scheduled-task inventory as one of the items to verify.
A minimal migration example
Consider a nightly backup currently scheduled with 30 2 * * * /usr/local/bin/backup.sh. The equivalent systemd arrangement is two files.
The service, /etc/systemd/system/backup.service:
[Unit]
Description=Nightly backup
After=network-online.target
OnFailure=notify-failure@%n.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
MemoryMax=1G
The timer, /etc/systemd/system/backup.timer:
[Unit]
Description=Run backup nightly
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
Enable it with systemctl enable --now backup.timer, confirm with systemctl list-timers, and test the service on its own with systemctl start backup.service before trusting the schedule. The [email protected] template referenced above is not provided by systemd. It must be written to send whatever alert your team actually monitors, which is the maintenance overhead noted earlier.
The migration is a change of paradigm, from a flat line to a service-timer architecture, and it should be tested on a non-production host first. Our guide on Linux automation with systemd timers, canaries and safe rollouts describes how to introduce such changes progressively, and the principle of staging before production applies equally to Ansible-driven hardening, which is a common way to distribute timer units across many hosts.
When cron is still the right answer
Cron retains real advantages. It is a lightweight, portable scheduler, present on essentially every Unix-like system, including those that do not run systemd. For a trivial script on a single host, such as rotating a scratch directory or logging a performance sample, one crontab line is faster to write and easier to hand to a colleague who has never opened a unit file. Practitioners note that for quick tasks without dependencies cron works well, and that the extra files of a timer buy little when failure is cheap.
Portability is the other consideration. Teams that maintain scripts across Linux, BSD and macOS hosts, or that ship software to environments they do not control, cannot assume systemd exists. In those settings a crontab entry is the common denominator.
When systemd timers are the better answer
Timers are the stronger option when any of the following holds:
- The job must be observable, with logs and exit status retained and queryable.
- The host may be off or rebooting at the scheduled time and the job must still run.
- The job depends on other units, such as a mount, a database or the network.
- The job is heavy enough to need CPU or memory limits, or sensitive enough to need sandboxing.
- Many hosts run the same schedule and the load should be spread.
The LinuxToday guide frames this as an environmental distinction: cron for portable, set-and-forget execution, timers for production servers where observability, failure reporting and resource limits are critical. Mixed estates are normal, and nothing prevents running both on one machine.
The systemd controversy and non-systemd systems
Systemd has been the default init and service manager of most major Linux distributions since about 2015, yet objections persist. Analyses of the init debate identify the principal criticism as a larger blast radius: init, logging, timers and name resolution are tightly integrated, so a change or update in one layer can affect unrelated services, and failure states are harder to reason about than with modular, minimal Unix tools. Alternatives such as OpenRC, runit, s6 and dinit remain actively maintained and serve distributions including Gentoo, Alpine, Void Linux and Devuan.
The consequence for scheduling is direct. On such systems timers are unavailable, and a traditional cron daemon must take over; a guide to migrating from systemd to OpenRC lists installing a cron implementation as a required step for tasks that timers previously handled. Organizations that run mixed init systems, or that are weighing whether to leave systemd, also need operational support for the resulting differences; managed infrastructure and security services are one route for teams without the in-house capacity to maintain both approaches.
Our perspective
We treat scheduled jobs as a reliability and security surface, not as a convenience. In the infrastructure automation and risk reviews we perform, unattended jobs are among the least examined components of a Linux host: they run with broad privileges, they fail silently, and their owners frequently no longer work at the organization. Those properties, rather than syntax preferences, are what we assess.
On that basis our position is conditional and consistent. Where a job is consequential (backups, certificate renewal, log shipping, data exports), we favor timers, provided that failure notification is configured at the same time. A timer without OnFailure= simply relocates silence from one file to another. Where a job is trivial and the host is long-lived and single-purpose, we see no benefit in rewriting a working crontab, because change itself carries risk and risk should be spent where it buys observability.
We also regard the systemd blast-radius criticism as legitimate and not as ideology. Tight integration is an operational trade-off, and teams should adopt timers knowingly, pin versions, and stage updates before production. The same discipline we recommend for configuration management applies here: test, canary, then roll out.
— Jesse Hart
Support for scheduling and automation work
If an audit of scheduled jobs, a cron-to-timer migration or a broader review of Linux automation is on your agenda, PROJECT-JTH offers Infrastructure Automation & Review and Technical Systems & Risk Review under technical consulting. Teams that manage client work and contracts can also look at Project Relay, listed at $49 per month, for production records and contract management.
FAQ
Is cron obsolete?
No. Cron remains widely deployed and is the most portable option across Unix-like systems. It is less capable than timers in logging and catch-up behavior, but for simple scripts those capabilities are often unnecessary.
What replaced cron?
Nothing has replaced it outright. On systemd-based distributions, timers are a native alternative that many administrators prefer for production work, and on distributions without systemd a cron daemon is still the standard scheduler.
Do systemd timers send email when a job fails?
Not by default. There is no built-in equivalent of cron’s MAILTO, so you configure an OnFailure= directive that starts a notification service, such as a unit that sends mail or posts to a monitoring channel.
Can cron and systemd timers run on the same server?
Yes. They are independent mechanisms, and many hosts use cron for trivial legacy scripts while timers handle jobs that need logging, dependencies or resource limits. Migrating incrementally, beginning with the most consequential jobs, is the lower-risk path.
Sources
- Cron vs systemd Timers: Scheduling Jobs on Linux, LinuxToday.net
- systemd/Timers, ArchWiki
- crontab(5), Linux manual page
- crontab(1), Linux manual page
- Bug 82084, freedesktop.org
- How do I handle time zones and daylight saving time in cron, Inventive HQ
- systemd vs SysVinit (and friends), System Administration
- The Ultimate Guide to Switching from Systemd to OpenRC, The CyberSec Guru
