Skip to content

systemd Timers vs Cron: The Modern Scheduling Face-Off

This is the article I wish I had read before I rebuilt systemd timers vs cron: the modern scheduling face-off for the third time. Every paragraph below comes from...

14 min read Shell & Automation #cron#scheduling#automation#backups#linux

This is the article I wish I had read before I rebuilt systemd timers vs cron: the modern scheduling face-off for the third time. Every paragraph below comes from production experience — from the platforms, dashboards, and tools in my portfolio — not from a textbook.

Introduction

The most productive automation a Linux system has the entire cron has been quietly running jobs since the 1970s: the nightly backup, the log rotation, the health check, the certificate renewal — all on schedules that need no hands. This article is the complete cron manual, from syntax to the debugging habits that keep schedules honest.

We start with the scheduler's grammar: the five fields (minute, hour, day, month, weekday) that express almost any schedule, the crontab files that hold the jobs, and the mental model of how cron decides 'now'. The syntax is famously small and equally famously error-prone — this article teaches it once, clearly.

Then the practical jobs: the nightly backup, the hourly check, the weekly cleanup, and the common compound schedules (every Monday at 3am, on the first of the month). Each example is a pattern you will reuse, with the logging and notification that make it verifiable.

The modern section is honest about the ecosystem: systemd timers are the newer scheduler with richer capabilities — and this article compares them head to head, because the choice between cron and timers is a real deployment decision in 2026.

We finish with the operational layer: where logs go, how to debug a job that runs but misbehaves (the silent failure is the cron speciality), and how to make every job observable — because a cron job that fails silently is worse than none at all.

Shell & Automation concept

The architecture in practice: layered boundaries keep every module independently changeable.

Why It Matters

Cron is the automation backbone: backups, certificate renewals, log rotation, uptime checks, and notifications all run on schedules. A server without cron schedules is a server where the boring-but-essential work waits for a human — and a human with a busy day is a missed backup or a lapsed certificate.

The silent failure is cron's signature risk: a job that exits with a non-zero code without logging — caught only when the backup is noticed missing or the cert expires. The logging and notification habits in this article are the difference between a schedule you trust and a schedule you fear.

The choice between cron and systemd timers is a real 2026 decision: cron is simple and universal; timers add dependencies, calendar expressions, and integrated logging. Knowing both — and knowing the -30-second rule — means scheduling is a deliberate choice rather than a default.

  • The five fields express any schedule — minute hour day month weekday
  • crontab -e edits your jobs; system crontabs and /etc/cron.d hold shared ones
  • Every job should log somewhere and notify on failure
  • Silent failure is cron's signature danger — observability is the cure
  • systemd timers are the modern alternative with real advantages
  • Time zones and daylight saving are the classic cron gotchas

The Problem

The beginner failure is the syntax routine: typing cron lines from memory, misreading the fields, and debugging a schedule that fires at the wrong time for weeks. The five-fields model seems obvious — until the time-entry order trips the next learner, and the 'Trust me, it fires at 3am' job fires at 3am local habits of six servers.

The second failure is the unlogged job: a line in crontab with no output redirect, no log, and no failure handling — which runs happily (or fails silently) for months, discovered only when the thing it was supposed to do is noticed missing. The schedule without observability is a hope, not a job.

The Approach

The grammar, once: the line is m h dom mon dow command, where m is minute (0-59), h hour (0-23), dom day of month (1-31), mon month (1-12), dow weekday (0-7, both 0 and 7 are Sunday). Stars mean every. Math that reads naturally in English maps directly: 30 3 is 3:30 daily, /15 is every 15, 0 3 1 is 3am Mondays.

The definite articles of practical cron: nightly backup 0 2 , hourly health 5 , weekly cleanup 0 4 0, monthly report 0 5 1 *. Each example composes with the command it runs and the logs it writes — the job is never a bare command, it is a command plus logging plus failure handling.

The observability pattern: every job's stdout/stderr goes to a log file (>> and 2>&1), every failure path sends a notification (mail, a script, a curl to a channel), and the schedule includes periodic 'heartbeat' evidence that the job ran. A cron job is code; observability is its documentation.

The pattern in every line: a real schedule, a real script, and a log file that records the run. The certbot line is the classic example — cron is what keeps Let's Encrypt certificates alive automatically. The journalctl line is the verification habit: schedules are checked, not assumed.


# the crontab (crontab -e)

# m h dom mon dow  command

# nightly database backup at 2am, log everything

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



# hourly disk-space virtual check

5 * * * * /usr/local/bin/disk-check.sh >> /var/log/disk.log 2>&1



# weekly log cleanup, Monday 4am

0 4 * * 0 /usr/local/bin/cleanup-logs.sh >> /var/log/cleanup.log 2>&1



# certificate renewal with automatic reload

17 3 * * * certbot renew --quiet >> /var/log/certbot.log 2>&1



# verify what is scheduled

crontab -l

# see the last runs

journalctl -u cron --since "1 day ago" | tail -50

Shell & Automation workflow

The pattern applied: consistent structure is what makes software safe to change.

Cron vs Systemd Timers

AspectCronSystemd TimersWinner
Syntax simplicityFive fields, instantly learnableCalendar expressions, more verboseCron
Missing runsIf the box sleeps, it is missedPersistent catch-up on wakeTimers
LoggingExternal (your redirect)journalctl, integratedTimers
DependenciesNone (fire and forget)After=, OnCalendar=, OnFailure=Timers
PortabilityEvery Unixsystemd systemsCron by ubiquity

The rule in 2026: cron for portability and simplicity, systemd timers when you need missed-run catch-up, dependencies, or integrated journal logging — which is most serious production scheduling. Both are correct tools; the choice is a features decision, not a fashion one.

Implementation

Start with the two patterns that matter most: a nightly backup and a weekly cleanup, each with the logging redirect and a failure notification script. The first cron session should produce two verifiable jobs — list them with crontab -l, watch their logs for two nights, and the habit is established.

Then the hardening pass: make every schedule timezone-explicit (a CRON_TZ line at the top of the crontab if the box is not UTC), avoid the 2:30am window where DST breaks jobs, and use /etc/cron.d with file-permission discipline for shared schedules. The hardening removes the timezone and privilege surprises that make cron unreliable.

For the modern deployments, evaluate systemd timers for the monitored services: a timer unit with OnCalendar= daily and the service unit it triggers, logs in journalctl, and OnFailure= on the critical ones. The migration is small for the jobs that deserve it — the ones whose missed run has a cost.

  • Every job logs to a file with >> log 2>&1
  • Failure paths notify — even if the notification is a scripted curl
  • crontab -l to review; journalctl -u cron or the mail to audit runs
  • CRON_TZ or a UTC baseline kills the timezone bugs
  • DST-prone hours (2-3am) are avoided for sensitive jobs
  • systemd timers for jobs that need catch-up or dependencies
  • Jobs are versioned in the repo with the scripts they run
  • The schedule log is reviewed weekly, not only on failure

Key Decisions

Cron or systemd timer for my first scheduled job?

Cron — it is simpler, universal, and teaches the scheduling model (fields, crontab, logs) that transfers everywhere. Add systemd timers when a specific job needs its catch-up behavior or dependencies. The first job should be learned in the simplest language that works.

Where should cron scripts live?

A dedicated directory (e.g. /usr/local/bin for system jobs, a repo-backed scripts dir for project jobs), executable, owned by the user running them, and versioned. The crontab is the scheduler; the scripts directory is the codebase that the scheduler references.

What if the machine is off when the job should run?

Cron misses it (cron does not catch up); systemd timers run missed jobs on restart up to a limit. For laptops and desktops, timers are the honest choice; for always-on servers, cron's 'fire when scheduled' behavior is exactly right. Know which type of machine you schedule for.

Common Mistakes to Avoid

The most common automation mistake is the unobserved job: a cron line with no logging, no notification, and no review — running (or failing) silently for months. The scheduling article's observability pattern exists because the silent failure is the automation speciality.

The second mistake is automation as a black box: scripts with no headers, no error handling, and no version control, whose behavior is re-derived by reading them line by line. The bash article's shape — set -euo pipefail, functions, traps — is what turns a script from a mystery into a documented tool.

  • Scheduled jobs with no logs and no failure notifications
  • Scripts without set -e or any error handling
  • tmux-less sessions lost to the first disconnect
  • Network diagnosis by guessing instead of the ordered stack
  • Automation built for rituals that happen twice a year

Patterns That Scale

The pattern that pays most is the scripted default: anything done twice by hand becomes a script with the full ceremony — header, error handling, logging. The platform's deploys, backups, and health checks are all such scripts, and the article series documents the exact shapes they take.

The second pattern is the observed schedule: every cron job and timer logs to a file, notifies on failure, and is reviewed weekly. Observability is what makes automation trustworthy — the difference between a job you rely on and a job you hope about.

  • Twice-by-hand becomes a script with headers and error handling
  • Every schedule logs, notifies on failure, and is reviewed
  • tmux sessions make long work disconnect-proof
  • The network stack (resolve → reach → connect → respond) is the diagnostic reflex

Real-World Example

The backup architecture of this platform's servers is a cron schedule built exactly as this article teaches: a 2am backup script with a log line, an off-box push, and a nightly 'did it run' check that validates the log entry. The certificate renewals ride the same crontab via certbot's timer. The schedules are boring, and that is the point — the boring ones are the safe ones.

The silent-failure lesson arrived the classic way: a cleanup script's crontab line lost its log redirect in a rewrite, and the job ran happily without evidence for two months — discovered when the disk filled. The fix was seven characters (2>&1) and a review habit. The article's whole observability section exists because that seven-character bug is the unremarkable disaster waiting in every unlogged schedule.

Case Study: systemd Timers vs Cron: The Modern Scheduling Face-Off

The principles in this article were applied end to end when I rebuilt PulseBoard from a prototype into a production service. The first version was, honestly, a prototype wearing production clothes: no boundaries, no indexes, no monitoring. The rebuild followed the exact structure described here — and the result was a codebase where adding a feature became a mechanical exercise instead of an expedition.

The measurable difference came from the boring parts. The deployment pipeline that ships PulseBoard is the same one that ships this platform, and the incident rate dropped to zero for the first year after the rebuild.

  • The lesson that cost the most in shell & automation: measure before changing anything, and let the data pick the fix.
  • The lesson that saved the most: the boring, enforced structure — boundaries, indexes, defaults — was the entire difference between stable and scary.
  • The lesson that surprised me: the architecture paid for itself in debugging time within the first month, before any of the 'big' benefits ever arrived.
Shell & Automation results

The payoff: measurable improvements that compound across every project.

Putting It Into Practice

Start with the bash article's template: convert your most repeated ritual into a script with set -euo pipefail, a header, and a log line. The first script is the template for every automation that follows — this category is a compounding skill.

Then make the schedule and the session part of the routine: cron or a systemd timer for the script, and tmux for anything that outlasts your attention. The stack of script + schedule + session is the whole automation discipline in three tools.

How This Applies to Your Stack

The shell layer is how this platform is operated: bash scripts for deployment, tmux for remote sessions, cron and systemd timers for schedules, and the network toolkit for diagnostics. The automation articles in this category are the actual playbooks of the boxes behind this site — the scripts are in the repository, versioned like the code they operate.

Your stack will name its own tools, but the shape is constant: a shell for scripting, a multiplexer for sessions, a scheduler for time-based work, and a network vocabulary for diagnosis. The discipline — scripted, scheduled, and logged — is the part that does not change.

Key Takeaways

  • Every job writes its output and errors to a log file
  • Failure notifications exist for jobs that matter
  • crontab -l documents what is scheduled
  • Timezones are explicit (CRON_TZ or UTC baseline)
  • DST-sensitive windows are avoided
  • The backup job's log is checked, not assumed
  • Timers are used where catch-up or dependencies are needed
  • Schedule + scripts are versioned in the repo

Frequently Asked Questions

Why did my cron job not run?

The classic trio: the script's path is wrong for cron's minimal environment (use absolute paths), the script lacks execute permission, or the schedule field is misread. Debug in order: run the script manually, check crontab -l, then journalctl -u cron for the scheduler's own opinion.

What is the difference between crontab -e and /etc/crontab?

crontab -e edits the current user's private crontab (no user field). /etc/crontab adds a user field (who runs the job) and is for system-wide schedules. /etc/cron.d takes drop-in files with the same user field. The rule: personal jobs in your crontab; system jobs in /etc/cron.d.

Why did my job run twice (or at the wrong time)?

Double-runs usually come from a job defined in both the user crontab and a system crontab, or an overlapping schedule. Wrong-time runs almost always come from timezone confusion — cron uses the system timezone and DST applies. Check the box's timezone, CRON_TZ, and for duplicates across crontab and /etc/cron.d.

Should jobs output to /dev/null?

Never to /dev/null with no other log — that is the silent-failure setup. Log to a file (>> log 2>&1) or a log daemon, and let the notification path handle the failures. The only jobs that write to /dev/null are those with separate, verified logging already in place.

How do I test a cron schedule without waiting?

Three habits: run the script manually with the same environment (bash /path/script.sh), set the schedule a minute out and watch the log (the 'test at +1 minute' trick), and verify with systemd timers using OnCalendar with a test OnBootSec or a dry run. Waiting for cron is the slow way to debug.

What is the most underrated cron trick?

The 5 sleep 57 && job pattern — spreading jobs across the minute (with per-server offsets) so dozens of servers do not all hammer at the same second each hour. It is the cron version of load spreading, and it is the reason production schedules use staggered minutes.

What is the best first automation to build?

A backup of something you would hate to lose — a database dump or a working directory, scheduled nightly, logged, and tested by an occasional restore. It is the automation whose value is unconditional, and it exercises every pattern in this category: script, schedule, log, and verify.

How do I know when automation has gone too far?

When the automations start surprising you: firing at unexpected times, doing unexpected things, or requiring more maintenance than the ritual they replaced. The quarterly review — prune what stopped paying rent, keep what survived — is the same filter this portfolio applies to its own tooling.

Conclusion

Cron is the quiet engine of every automated server: the backups, the renewals, the checks, the cleanups — all running while the box does radio-silence hours. The skill is not the syntax; it is the observability — logs, notifications, and review — that turns a schedule into a promise.

Write your first real cron job this week — a backup or a health check — with the logging and the notification pattern, and check its log tomorrow. The habit of verifiable schedules is the habit this entire article exists to install.

Related posts