A systemd timer that runs twice after a reboot
A systemd timer that runs twice after a reboot
After every reboot, your 03:10 backup starts twice. Two processes race for the same lock, one exits with “another instance is running,” the timer looks healthy, and you get a pager alert plus a half-finished upload. The cause is usually not the job itself: it is two independent activation paths.
Diagnose the duplicate before editing units
First prove there are two starts, not one start and a retry:
journalctl -u backup.service --since "30 min ago" -o short-precise
systemctl list-timers --all 'backup*'
systemctl status backup.service backup.timer --no-pager
Then inspect the units and enablement:
systemctl cat backup.timer backup.service
systemctl is-enabled backup.timer backup.service
ls -l /etc/systemd/system/*.wants/backup.*
Look for two things: a service that is enabled separately from its timer, and a timer with both a calendar schedule and a boot-relative trigger. Also check for two timers that both start the same service.
Why a systemd timer persistent duplicate run happens after reboot
With Persistent=true, systemd records the last trigger and fires the timer when it becomes active if the scheduled time passed while the machine was down [link needed]. That catch-up run is intentional. A systemd timer persistent duplicate run appears when that catch-up is added to another start path.
The common case is a service with [Install] WantedBy=multi-user.target that is also enabled. At boot, systemd starts the service directly; moments later, the persistent timer catches up and starts it again. A timer with both OnCalendar= and OnBootSec= can produce the same effect, because both triggers are active. Two timers with overlapping schedules and the same Unit= are another source.
Stop the duplicate with this procedure
1. Decide which unit owns the schedule. If backup.timer has the OnCalendar= entry, the timer should start the service; the service should not also be enabled at boot.
2. Disable the service’s independent boot start, keeping the timer enabled:
```bash
sudo systemctl disable backup.service
sudo systemctl daemon-reload
sudo systemctl restart backup.timer
```
Do not mask the service unless you also intend to block timer starts.
3. If the timer itself has two triggers, remove the redundant one. For a scheduled backup, keep OnCalendar= and Persistent=true; remove OnBootSec= unless you need an additional boot-relative run.
4. If a second timer targets the same service, disable the duplicate timer:
```bash
sudo systemctl disable --now backup-extra.timer
```
5. Validate without waiting for the calendar: check systemctl list-timers --all, run systemctl start backup.service once manually, then reboot and compare journalctl timestamps. If you are testing catch-up behavior, inspect /var/lib/systemd/timers/ for the timer’s stamp file [link needed].
When NOT to do this
Do not disable the service if other units pull it in with Wants= or Requires=, or if boot-time execution is required even when the timer is inactive. Do not remove Persistent=true just to hide duplicates: that also removes catch-up after downtime. And if the service is socket-activated or started by a path unit, disabling its boot start may not remove the second path; fix the actual trigger instead.
Checklist
- Two starts confirmed in
journalctl, not assumed. systemctl catchecked for service enablement and duplicate timer triggers.- Timer owns the schedule; service is not also enabled at boot.
daemon-reloadrun after unit changes.- Reboot test shows one start, with the expected persistent catch-up behavior.