If your backup must still run after the server was off at 3:00 AM, plain cron is usually the wrong default. A systemd timer with OnCalendar= and Persistent=true is safer because it can catch a missed wall-clock run the next time the host starts. Standard cron is still fine for simple, stateless housekeeping on machines that are always up. For real backup jobs, especially on VPS instances that reboot for kernel updates or maintenance, systemd timers are usually the cleaner and more reliable choice.
The short decision rule
- Use cron when the job is simple, the server is effectively always on, missing one run is acceptable, and you want the least moving parts.
- Use a systemd timer when the job matters, the machine may be rebooted or stopped overnight, you want better visibility through
systemctlandjournalctl, or you need cleaner execution control. - For backup jobs, default to systemd timers unless you have a specific reason not to.
That is the practical answer. The rest is about avoiding the traps.
What actually happens when the server is down at 3am
Plain cron checks schedules minute by minute while the cron daemon is running. If the host is powered off at the scheduled time, that specific run is missed. Cron itself does not replay it when the machine comes back. This is the part many guides dance around. They say cron is proven, which is true, but they quietly skip the downtime semantics.
Anacron exists to soften that problem for jobs that run daily, weekly, or monthly. On many Linux systems, that is how the classic /etc/cron.daily, /etc/cron.weekly, and /etc/cron.monthly flow is handled. That still does not mean every user crontab entry magically becomes persistent. If your backup lives in a personal crontab or a custom line in /etc/cron.d/backup, you need to know exactly which component is responsible for catch-up behavior.
Systemd timers are more explicit. If you schedule the job with OnCalendar=*-*-* 03:00:00 and set Persistent=true, systemd stores the last trigger time on disk and starts the service immediately after boot if at least one scheduled run was missed while the timer was inactive. That is the feature backup admins actually care about.
Why this matters more for backups than for cleanup jobs
A missed log cleanup is annoying. A missed backup window can turn a recoverable incident into data loss. In our experience managing production Linux systems, this difference is where the cron versus timer debate stops being academic.
Take a small VPS that reboots at 02:57 after kernel patching. If your backup is supposed to run at 03:00, plain cron may miss that run completely depending on how fast the host returns and whether the exact minute boundary is gone by the time the daemon is up. A persistent systemd timer will catch that missed schedule after boot. That is the whole point.
On storage-heavy backups, this gets worse. A WordPress stack dumping MariaDB, walking a large wp-content/uploads tree, and sending data off-site can already be the noisiest job on a small Linux box. If that job is important enough to care about, it is important enough to schedule with something that has explicit missed-run behavior.
When cron is still good enough
Cron is not obsolete. It is still the right answer in a few very common cases.
- The task is tiny and idempotent, like pruning a temp directory or rotating an application cache that can wait until tomorrow if needed.
- The machine is effectively always up, like a stable VM with rare reboots and no overnight shutdown pattern.
- You are working inside a legacy estate where cron is already the accepted operational standard and the team knows exactly how it behaves.
- The job is already protected against overlap and failure, and the business impact of a missed run is low.
If that is your reality, cron is still perfectly serviceable. The mistake is pretending that a backup job has the same risk profile as deleting old files from /tmp.
When a systemd timer is the safer choice
Use a timer when the job has real operational weight. Backups, snapshot exports, off-site sync jobs, certificate maintenance, integrity checks, or database dumps belong here.
- You want post-reboot catch-up with
Persistent=true. - You want the scheduler and the job to live in explicit unit files under
/etc/systemd/system/. - You want clean introspection with
systemctl list-timers --all,systemctl status, andjournalctl -u. - You want execution conditions and dependencies that are less fragile than shell glue inside a crontab line.
- You want the service model to help prevent accidental re-entry while the previous invocation is still active.
This is one of the places where managed support saves time. If you need this built and validated on production hosts, ServerSpan’s Linux administration service handles timer migration, backup scheduling, and reboot validation without turning it into an overnight guessing game.
The overlap problem that breaks more backups than the scheduler does
Here is the ugly truth: the scheduler is often not the failure point. The real failure is overlap.
A backup that starts at 03:00 and is still running at 03:00 the next day is already telling you something is wrong. But shorter overlaps are common too. Think of a backup job that normally finishes in 12 minutes, then takes 85 minutes during a slow object-storage window. If cron fires again before the previous run is done, you can get duplicated dumps, locked repositories, corrupted temp state, or I/O pressure that wrecks the box.
Systemd helps here because if the service unit is still active when the timer elapses, systemd does not spawn a second instance of that same service. That is better default behavior than a naive crontab line. But do not get lazy. The safest pattern is still to use a lock.
For shell-driven jobs, flock is the simplest answer. Use it in cron. Use it in ExecStart=. Use it anywhere the backup command might be triggered twice by mistake.
A cron example that is acceptable for a simple backup
If you insist on cron, do not schedule the raw script directly. Wrap it with a lock and send output somewhere you actually read.
0 3 * * * /usr/bin/flock -n /run/lock/nightly-backup.lock /usr/local/sbin/backup-nightly.sh >> /var/log/backup-nightly.log 2>&1
That is the bare minimum. It does not catch a missed 3:00 AM run after downtime. It just stops you from launching two overlapping copies if the previous one is still running. For some environments, that is enough. For important backups, it usually is not.
A systemd timer example that is safer for real backups
Create a service file first. Keep the backup logic in the script. Keep scheduling in the timer. Keep overlap prevention in the unit invocation.
[Unit]
Description=Nightly backup job
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
User=root
ExecStart=/usr/bin/flock -n /run/lock/nightly-backup.lock /usr/local/sbin/backup-nightly.sh
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7
Save that as /etc/systemd/system/backup-nightly.service.
[Unit]
Description=Run nightly backup at 03:00
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
RandomizedDelaySec=5m
AccuracySec=1m
[Install]
WantedBy=timers.target
Save that as /etc/systemd/system/backup-nightly.timer.
systemctl daemon-reload
systemctl enable --now backup-nightly.timer
systemctl list-timers --all | grep backup-nightly
systemctl status backup-nightly.timer
journalctl -u backup-nightly.service -n 100 --no-pager
This gives you a 3:00 AM calendar schedule, a catch-up run after downtime, a lock to prevent overlap, and a clean operational workflow for inspection. That is why timers win for backups.
The trap people hit when they try to copy cron logic into timers
Do not confuse OnBootSec= with a persistent daily schedule.
OnBootSec=3h means three hours after boot. It does not mean “run at 3:00 AM every day and catch up if missed.” That distinction matters. If your backup must happen at a wall-clock time, use OnCalendar=. Then add Persistent=true if missed-run recovery matters.
Another trap is setting RemainAfterExit=yes on a repetitive oneshot service. That can make the service remain active in a way that prevents future timer firings from behaving the way you expect. For repetitive backup timers, keep the oneshot service simple and let it exit cleanly.
What neither cron nor a persistent timer does for you
Neither tool is a real job queue.
If the host is down for three days, a persistent timer does not usually replay three missed daily backups one by one. It catches up once because at least one scheduled run was missed. That is usually fine for backup workflows, because what you want is “resume protection quickly” rather than “replay every missed historical run.” But if you truly need every missed interval processed separately, you need stateful application logic, not just a scheduler.
How to migrate a backup job from cron to a timer without making a mess
- Inspect the current cron entry and the script it calls. Check for hidden assumptions about
PATH, shell, working directory, and output redirection. - Run the backup manually first. If the script is fragile when launched by hand, changing the scheduler will not fix it.
- Add a lock if the script does not already have one.
- Create the
.serviceunit and test it withsystemctl start backup-nightly.service. - Create the
.timerunit, but do not enable it until the service test is clean. - Disable the old cron entry before enabling the timer. Do not leave both active “for safety.” That is how duplicate backups happen.
- Reboot once in a controlled window if the job is supposed to survive post-maintenance reboots. Then verify
systemctl list-timers --alland inspectjournalctl -u backup-nightly.service.
If you are already tightening your backup posture, read our VPS backup strategies guide. If you are standardizing other maintenance jobs at the same time, our automatic Linux updates guide covers the same operational discipline from the patching side.
ServerSpan’s recommendation
For backups, use systemd timers by default. Use cron only when the job is trivial, the host is always on, and missing a run is not a real problem.
That is the blunt version. Cron is simpler. Systemd timers are safer for backup reliability. If your backup job has any real recovery value, the missed-run semantics alone justify the switch. Add a lock either way. Keep the script separate from the scheduler. Test after reboot. Then stop pretending a nightly backup is “set and forget.” It is not.
If you want this designed, migrated, and tested on a production Linux host, ServerSpan’s managed Linux administration service is the right handoff. If you are moving the workload onto a machine where you control scheduling, storage, and root access, a Linux VPS gives you the room to do it properly.
Source & Attribution
This article is based on original data belonging to serverspan.com blog. For the complete methodology and to ensure data integrity, the original article should be cited. The canonical source is available at: Cron vs systemd timers for backups: which one still fires after a reboot at 3am?.