Why my nightly rsync backup disappeared after switching to a systemd timer – and how I fixed it with 'Persistent=true

Nightly rsync vanished after moving to systemd timers

I had a simple nightly backup script that ran under cron for years. One evening I decided to modernise the scheduler: replace the crontab entry with a systemd timer. The idea was clean, the logs would be in journalctl, and I could keep the backup logic in a single unit file. A few days later the backup disappeared from the destination directory. After a quick audit I discovered that the timer had never fired because the system had been powered off at the scheduled time. The culprit was the missing Persistent=true setting.

Below is a step‑by‑step walk‑through of what happened, how I diagnosed the issue, and the exact change that fixed it. I’ll also touch on the trade‑offs of using systemd timers for backups and a few security niceties that come with the setup.


The original cron setup

# /etc/cron.d/rsync-backup
0 2 * * *   root   /usr/local/bin/rsync-backup.sh

The script was a thin wrapper around rsync:

#!/usr/bin/env bash
set -euo pipefail

SRC="/home/user/data"
DST="[email protected]:/mnt/backups/user-data"

rsync -avz --delete "$SRC/" "$DST/"

The cron job worked fine. The logs were in /var/log/cron, and I could check the destination with ssh [email protected] ls -l /mnt/backups/user-data.


Switching to systemd

I created two unit files in /etc/systemd/system/:

# /etc/systemd/system/rsync-backup.service
[Unit]
Description=Nightly rsync backup
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/rsync-backup.sh
# /etc/systemd/system/rsync-backup.timer
[Unit]
Description=Run rsync backup nightly

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=false

[Install]
WantedBy=timers.target

After systemctl daemon-reload and systemctl enable --now rsync-backup.timer, I expected the same behaviour as before. However, after a weekend of power outages, the backup directory was empty.


Why the timer missed the job

Systemd timers are event‑driven. When the system boots, timers that are not marked as persistent are disabled until the next scheduled time. If the scheduled time has already passed while the machine was powered off, the timer will not fire until the next day. This is intentional: it prevents a burst of jobs from running immediately after a reboot.

In my case, the machine was down from 01:30 UTC to 03:15 UTC on a Saturday. The timer was set for 02:00 UTC. Because Persistent=false (the default), the timer never queued a job for the missed slot. The next run happened at 02:00 UTC on Sunday, and I had a week‑long gap in the backup history.


The fix: Persistent=true

Adding Persistent=true tells systemd to remember missed events and trigger them as soon as the system is back online. The timer file becomes:

# /etc/systemd/system/rsync-backup.timer
[Unit]
Description=Run rsync backup nightly

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true

[Install]
WantedBy=timers.target

Reload and restart:

sudo systemctl daemon-reload
sudo systemctl restart rsync-backup.timer

Now, if the machine is down during the scheduled time, the backup will run immediately after boot. journalctl -u rsync-backup.service shows a log entry with the timestamp of the missed run, which is useful for audit trails.


Diagnostics checklist

Step What to check Command
1 Timer status `systemctl list-timers –all
2 Last run time systemctl show rsync-backup.timer --property LastTriggerUSec
3 Service logs journalctl -u rsync-backup.service -b
4 Network availability systemctl status network-online.target
5 File permissions ls -l /usr/local/bin/rsync-backup.sh

If the timer shows LastTriggerUSec=0, it means no run has ever occurred. If the service logs show “Failed to connect to backup server”, the issue is network‑related, not timer‑related.


Trade‑offs of using systemd timers for backups

Aspect Cron Systemd Timer
Granularity Seconds‑level via */5 * * * * Seconds‑level via OnCalendar or OnUnitActiveSec

Tags

  • linux
  • systemd
  • rsync
  • backup

See also