skip to content

Your team runs its nightly jobs from crontab entries and is considering moving them to systemd timer units. What do timers actually give you that cron does not, and what do you give up?

level: middleimportance: must knowfreq 70%

answer

  1. one line versus a supervised unit
  2. where does the output go?
  3. cgroup buys limits and cleanup
  4. list-timers answers a fleet question
  5. the cost is boilerplate and portability

basics

~20 s

Timers make each job a supervised unit: output lands in the journal, exit status is recorded, the job runs in its own cgroup with resource limits, it can be ordered against other units, and systemctl list-timers shows every schedule on the host. The cost is two files per job and no portability off systemd.

solid answer

~60 s

The real argument for timers is that the job becomes a normal service unit. Its stdout and stderr are captured by the journal instead of being mailed to whoever owns the crontab and dropped when no MTA exists, and its exit status is recorded so you can see whether last night's run failed. It runs in its own cgroup, so you can cap it with `MemoryMax=` or `CPUQuota=` and so all of its children are tracked and cleaned up rather than orphaned. Because it is a unit, it can be ordered against other units with `After=` rather than guessing at boot with a sleep. systemd will not start a second copy while the previous run is still active, so slow jobs do not stack up without a lock file. And `systemctl list-timers` answers "what runs when on this host" in one command, across system units and per-user units. What you give up: two files instead of one line, a steeper syntax, and nothing that works off a systemd host.

code

ini · 10 lines
ini
[Unit]
Description=Nightly search reindex
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/reindex.sh
User=indexer
CPUQuota=40%
MemoryMax=1G

go deeper

for a junior

Know that both schedule recurring work, and be able to name two concrete timer advantages — output goes to the journal and systemctl list-timers shows every schedule.

for a middle

Explain the mechanics behind each advantage: journal capture and recorded exit status, the per-job cgroup, no concurrent activation of an active unit, and ordering against other units.

for a senior

Talk about operating this at scale — inventorying forgotten jobs, bounding a nightly job's CPU and memory so it cannot page someone, and rehearsing a job with systemctl start in its real environment.

for a principal

Own the migration decision: which classes of jobs justify the boilerplate, what portability you are trading away, and how you keep a half-migrated fleet from having two places to look for scheduled work.

## What cron actually is A cron daemon reads crontab files — the per-user ones edited with `crontab -e`, plus `/etc/crontab` and drop-ins under `/etc/cron.d` — and forks a shell to run a command line at matching minutes. That is the whole model, and its virtues are real: one line per job, portable to every Unix, understood by everyone. Its limits are all consequences of the same minimalism — the daemon has no idea whether your job succeeded, how long it ran, what it spawned, or whether the last copy has finished. ## Where the output goes Cron mails a job's stdout and stderr to the crontab's owner (or to `MAILTO=`). On a modern server with no local mail transfer agent installed, that output is simply lost, which is why so many cron jobs end with a hand-rolled `>> /var/log/thing.log 2>&1` and a separate logrotate config to stop it eating the disk. A systemd service's stdout and stderr are captured by the journal by default. You get the job's output, its start and stop timestamps and its exit status recorded against the unit, retained under the journal's own size limits, with no redirection and no extra rotation config. Failure becomes queryable rather than something you notice a week later. ## Ordering and dependencies Cron has `@reboot` and nothing else; a boot-time job that needs the network is traditionally handled with `sleep 30` and hope. A timer-triggered job is a unit, so it can declare ordering against other units — `After=network-online.target`, for example — and systemd resolves that instead of you timing it by hand. ## Resource control and process hygiene Every systemd service runs in its own cgroup. Two things follow. First, you can constrain it declaratively in the `[Service]` section: ```ini [Service] Type=oneshot ExecStart=/usr/local/bin/reindex.sh CPUQuota=40% MemoryMax=1G IOWeight=20 ``` A nightly reindex that used to page someone at 03:00 by saturating the box is now bounded. Second, everything the job forks stays in that cgroup and is accounted to it and cleaned up with it. Cron's children, by contrast, are just processes; a job that leaks a background helper leaves it running until someone notices. ## Overlap If a cron job scheduled every five minutes takes seven, you get overlapping copies, and the standard fix is your own lock (`flock`). systemd will not run a second instance of an already-active unit, so a slow run delays the next trigger rather than stacking on top of it. ## Visibility ```bash systemctl list-timers --all ``` One command lists every timer with its next elapse, time remaining, last elapse and the unit it activates. The cron equivalent is walking every user's crontab plus `/etc/crontab` plus `/etc/cron.d` plus the `cron.daily`/`cron.hourly` directories — which is exactly why forgotten cron jobs are a genre of incident. ## Running it by hand With cron, testing a job means copying the command line out of the crontab and running it in your shell, where PATH, environment, working directory and user are all different from the real thing — the classic "works when I run it, fails at 3am". With a timer, `systemctl start reindex.service` runs exactly the unit that the schedule runs, in the same environment. ## Catching a missed run If the machine is off at the scheduled minute, cron simply skips it; distributions bolt on anacron for the `cron.daily` class of jobs. A systemd timer with `Persistent=true` records the last trigger on disk and runs once shortly after the machine comes back. ## What you actually give up - **Boilerplate.** Two unit files and a `daemon-reload` per job, versus one crontab line. For a handful of trivial jobs that is a real cost. - **Portability.** Timers exist only where systemd does. A script that must also run on a BSD or a minimal container image needs cron or an in-process scheduler. - **Familiarity.** Five-field cron syntax is universal knowledge; `OnCalendar=` and monotonic timers are not, and a team that half-knows them will write schedules that do not mean what they think. - **A different environment, not a richer one.** Services do not inherit a login environment either; you still specify absolute paths, `WorkingDirectory=`, `User=` and `Environment=` explicitly. ## The honest answer in an interview For anything that matters operationally — jobs whose failure you need to see, jobs that need bounding, jobs that must be inventoried across a fleet — timers win, and the two files are worth it. For a personal one-liner on a box you own, cron is still fine, and saying so is a better answer than claiming timers are strictly superior.

  • A cron job that runs every five minutes sometimes takes seven. What happens, and how does the timer version behave differently?
    Cron starts a second copy while the first is still running, and they contend or corrupt shared state unless the script takes its own lock with `flock`. systemd will not activate a unit that is already active, so the trigger does not produce a concurrent run — the overlap protection is built into the unit model rather than into your script.
  • Your cron job writes to stderr and nobody ever sees it. What is happening, and what changes under a timer?
    Cron mails stdout and stderr to the crontab owner via a local MTA; on a server with no MTA installed the output is discarded. A service's stdout and stderr are captured by the journal by default and stored against the unit along with its exit status, so a failing run is visible without any redirection or extra log rotation.
  • When would you still choose cron over a systemd timer?
    When the host may not run systemd at all — a BSD, a minimal container image, or an appliance — when the job is a trivial personal one-liner whose failure has no operational consequence, or when a team owns hundreds of existing crontab lines and the migration cost outweighs the visibility gain for jobs nobody depends on.

saying these in an interview costs you the question

  • Claims timers are strictly better with no downsides
  • Thinks cron job output is written to syslog by default
  • Believes cron prevents overlapping runs of the same job
  • Says timers inherit your interactive shell environment
  • Assumes cron can express dependency ordering on other services

context