I went looking for this laptop's crontab to write a comparison with systemd timers, and there wasn't one. No crontab binary, no /etc/cron.d, no cron daemon running at all — crontab -l just says command not found. What actually runs this ThinkPad's periodic housekeeping, on the Linux From Scratch build it runs, is four systemd timers, installed by the packages that need them rather than written by hand:
NEXT LEFT LAST PASSED UNIT ACTIVATES
Sun 2026-10-18 02:00:00 CEST 4h 51min Sun 2026-10-11 11:00:14 CEST - update-pki.timer update-pki.service
Sun 2026-10-18 05:47:57 CEST 8h Sat 2026-10-17 03:18:34 CEST - man-db.timer man-db.service
Sun 2026-10-18 15:15:29 CEST 18h Sat 2026-10-17 12:56:50 CEST 5h 53min ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Mon 2026-10-19 00:24:44 CEST 1 day 3h Mon 2026-10-12 13:07:12 CEST - fstrim.timer fstrim.service
That's systemctl list-timers --all, and it's already a useful place to start, because it shows two things a crontab listing can't: a resolved next run time per job, and when each job last actually fired.
A timer is two files, not one line
A crontab entry is self-contained: schedule and command on the same line. A systemd timer splits that into a pair of unit files, linked by sharing a basename — the schedule in one, the actual work in another. Here's the real pair behind the daily man-db job on this machine, in full:
$ systemctl cat man-db.timer
[Unit]
Description=Daily man-db regeneration
Documentation=man:mandb(8)
[Timer]
OnCalendar=daily
RandomizedDelaySec=12h
Persistent=true
[Install]
WantedBy=timers.targetand the relevant lines of its paired service file:
[Unit]
Description=Daily man-db regeneration
Documentation=man:mandb(8)
ConditionACPower=true
[Service]
Type=oneshot
ExecStart=/usr/bin/mandb --quiet
User=bin
Nice=19
IOSchedulingClass=idle
IOSchedulingPriority=7
That split is more than bureaucracy. The service half declares its own conditions, user, and scheduling priority using the same unit syntax as any other systemd service, not a smaller grammar bolted onto a scheduler. ConditionACPower=true is doing real work here: rebuilding the man-page database is CPU-heavy enough that the job skips itself outright on battery. A cron job has no equivalent gate built in; checking the power source would mean the job's own script reading /sys/class/power_supply itself.
It isn't theoretical — it skipped a run tonight
The journal for this exact service shows it happening. The most recent entries end with:
okt 16 13:54:21 t14 systemd[1]: man-db.service: Deactivated successfully.
okt 16 13:54:21 t14 systemd[1]: Finished Daily man-db regeneration.
okt 16 13:54:21 t14 systemd[1]: man-db.service: Consumed 17.883s CPU time, 115M memory peak.
okt 17 03:18:34 t14 systemd[1]: Daily man-db regeneration skipped, unmet condition check ConditionACPower=trueTwo things there needed no setup on my part to produce. The run on the 16th recorded its own CPU and memory use, because systemd tracks resource accounting per service by default. And the run due in the small hours of the 17th simply didn't happen, logged with the exact reason, because the laptop was running on battery at 3am. Reproducing either half of that with cron means writing it yourself: wiring up timing and memory accounting around the job, and a battery check for the gate, and then giving the result somewhere to go — which is the next difference.
Where the "it ran" record lives
Cron's own activity logging depends on syslog; without a syslog daemon running, there's no record anywhere that a cron job even fired, only the job's own output, and only if the job redirects that output somewhere itself. I checked this directly rather than assume it, in a minimal Debian container with cron installed and no syslog daemon: a job scheduled for every minute, left running for about a minute, produced exactly one trace — the file the job itself was told to append to. Nothing in /var/log recorded that cron had run anything at all.
A systemd timer's service run goes into the journal by default, no logging setup required: start time, finish time, exit status, and the resource line shown above. For a job that only runs once a day or once a week, the lack of guaranteed logging is the kind of gap that stays invisible for months, right up until it matters.
Missed runs: cron forgets, a timer can remember
The other gap showed up when I simulated the laptop being off through a scheduled run. In the same test container, with a job scheduled for every minute, stopping the cron daemon for a little over two scheduled minutes and starting it back up produced nothing: no catch-up run, no trace that two runs had been missed. That matches cron's design — it only acts on the clock while it's actually running.
Persistent=true, set on all four timers on this machine, is what's missing from that picture. systemd.timer(5) describes it plainly: the service unit is triggered immediately if it would have been triggered at least once during the time the timer was inactive. systemd keeps a timestamp on disk for exactly this, so a timer that was due while the machine was suspended or shut down fires once as soon as it's back, instead of silently waiting for the next scheduled slot — which, for a weekly job like fstrim.timer, could mean waiting up to a week.
What crontab still gets right
None of this makes crontab the wrong choice generally. Its five-field syntax — minute, hour, day of month, month, day of week — is one line, portable across almost any Unix-like system, and needs no companion unit file for a one-off job. For a short script on a machine that doesn't otherwise run systemd, or a container where pulling in a full init system is the wrong trade, crontab is still the simpler tool. What changes is everything downstream of "the schedule fired": whether the run is logged anywhere without extra effort, whether a missed run is recoverable, and whether the job can gate itself on conditions like power source without hand-written logic. On a machine already using systemd units for everything else — like this one — a timer gets all three for free.
Leave a comment
Comments are reviewed before they appear.