Scheduler comparison

Cron vs systemd Timers
Which should you use?

Both schedule jobs. They differ in logging, dependencies, and what happens when things go wrong. Here's an honest comparison.

Side-by-side comparison

Featurecronsystemd timers
ConfigurationSingle line in crontabTwo files (.timer + .service)
Syntax5-field cron expressionOnCalendar= with calendar events
LoggingSyslog (one line per invocation)journalctl (full stdout/stderr)
DependenciesNone — runs independentlyCan depend on other units (After=, Requires=)
Randomized delayNot built inRandomizedDelaySec= built in
Missed runsSkipped silentlyPersistent=true catches up
Precision1-minute minimumSub-second with OnActiveSec=
PortabilityEvery Unix/Linux/macOSLinux with systemd only
User jobscrontab -e (per user)systemctl --user (per user)
EnvironmentMinimal PATH, set vars manuallyEnvironment= directive in unit file
Failure notificationEmail via MAILTO (if MTA configured)OnFailure= triggers another unit
Resource limitsNoneMemoryMax=, CPUQuota= via cgroups

When to use cron

  • You need something simple that works everywhere — Linux, macOS, BSD, containers
  • One-line schedule, no config files to manage
  • The job has no dependency on other services starting first
  • Your team already knows cron syntax
  • You’re in a container or minimal environment without systemd

Cron has survived 40+ years because it does one thing well: run a command on a schedule. If that's all you need, cron is the right choice.

When to use systemd timers

  • Your job needs to run after another service starts (dependency ordering with After=)
  • You want full stdout/stderr captured automatically in journalctl
  • You need randomized delay to avoid thundering-herd problems (RandomizedDelaySec=)
  • You want missed runs to catch up after reboot (Persistent=true)
  • You need resource limits (MemoryMax=, CPUQuota=) enforced via cgroups
  • You need sub-second precision or monotonic timers (OnActiveSec=)

systemd timers are more powerful but also more complex. You need two files instead of one line, and they only work on Linux distributions with systemd.

The real question isn't which scheduler

Both cron and systemd timers share the same blind spot: neither one alerts you when a job doesn't run. Cron silently skips missed runs. systemd's Persistent=true catches up after reboot, but if the timer unit is disabled or the service fails repeatedly, you find out when someone notices the downstream impact.

The scheduler is the easy part. Knowing that your jobs are actually running, completing successfully, and finishing in a reasonable time — that's the hard part.

CronDoctor works with both

Add the signal pattern to either scheduler. CronDoctor detects missed runs, captures error output, and diagnoses failures — regardless of how you schedule the job.

# Crontab (cron)
# Nightly backup with CronDoctor monitoring 0 3 * * * curl -sf https://crondoctor.com/api/v1/ping/YOUR_ID/start && \ /opt/scripts/backup.sh 2>/tmp/backup.err && \ curl -sf https://crondoctor.com/api/v1/ping/YOUR_ID/end || \ curl -sf -X POST https://crondoctor.com/api/v1/ping/YOUR_ID/fail \ -H "Content-Type: application/json" \ -d "{\"exit_code\":$?,\"stderr\":\"$(tail -5 /tmp/backup.err)\"}"
# systemd service unit
# /etc/systemd/system/backup.service [Unit] Description=Nightly backup [Service] Type=oneshot ExecStartPre=curl -sf https://crondoctor.com/api/v1/ping/YOUR_ID/start ExecStart=/opt/scripts/backup.sh ExecStartPost=curl -sf https://crondoctor.com/api/v1/ping/YOUR_ID/end
# systemd timer unit
# /etc/systemd/system/backup.timer [Unit] Description=Run backup nightly [Timer] OnCalendar=*-*-* 03:00:00 Persistent=true [Install] WantedBy=timers.target

Also works with Kubernetes CronJobs, AWS EventBridge, and any scheduler that can make an HTTP request. See all integration examples →

Try it with your scheduler

CronDoctor monitors your jobs regardless of how you schedule them. One curl command to set up.

Start free — no credit card