I’ve been migrating my crontabs to systemd’s timer units. They all look similar to this:
.timer file:
[Unit] Description=timer that uses myjob.service [Timer] OnCalendar=*-*-* *:00:00 Unit=myjob.service [Install] WantedBy=timers.target
.service file:
[Unit] Description=Script that runs myjob.sh [Service] ExecStart=/home/user/myjob.sh
My timers work but they also execute on system reboot. I would like my OnCalendar events to only run at the specified times, not whatever random time I reboot the PC. Any ideas?
UPDATE:
I resolved this problem by converting my ‘user’ timers into root/system timers.
- I disabled all of my .service and .timer files, and moved them out of my home directory into /etc/systemd/system.
- I added the ‘User=’ section to each service file, so that my scripts were ran by the regular user and not as root.
Now my timers aren’t being triggered on system startup and I was also getting problems with sporadic triggering when I logged in via ssh. This has also been solved now that they are under control of the root account but run my scripts are still run as the PID of regular user, which preserves my files’ ownership attributes. Problem solved.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
I was also having the same problem and could not figure out why units would always run at startup regardless of the Persistent setting in the .timer file and it took me a while but I finally found the cause (pointed in the right direction by @alexander-tolkachev’s comment).
The problem is that I have always included something like WantedBy=basic.target in the [Install] section of the .service file (because its part of the standard systemd service copy pasta). It turns out this actually causes the unit to be started whenever basic.target is (aka system boot).
https://www.freedesktop.org/software/systemd/man/systemd.unit.html#WantedBy=
https://www.freedesktop.org/software/systemd/man/systemd.special.html#basic.target
I suspect the OP inadvertently solved their issue by disabling the old .service (which you must do in order to remove the symlink created by WantedBy) and either omitting the [Install] section when they re-wrote it or never running systemctl enable.
TLDR; You do not want an [Install] section in a .service file that is triggered by a .timer file.
Method 2
According documentation you should change your config to Persistent=false or remove Persistent at all, because it false by default.
Method 3
I resolved this problem by converting my ‘user’ timers into root/system timers.
- I disabled all of my .service and .timer files, and moved them out of my home directory into /etc/systemd/system.
- I added the ‘User=’ section to each service file, so that my scripts were ran by the regular user and not as root.
Now my timers aren’t being triggered on system startup and I was also getting problems with sporadic triggering when I logged in via ssh. This has also been solved now that they are under control of the root account but run my scripts are still run as the PID of regular user, which preserves my files’ ownership attributes. Problem solved.
The OP posted this as an edit to the question, so I reproduced it here.
Method 4
While investigating this problem on my own server, I discovered the following:
$ systemctl status man-db.timer Apr 09 08:10:00 anemone systemd[1]: man-db.timer: Not using persistent file timestamp Mon 2018-04-16 19:40:02 EDT as it is in the future. Apr 09 08:10:00 anemone systemd[1]: Started Daily man-db cache update.
Turns out the battery for the on-board RTC was dead, so the system was booting with a date in the past (probably taken from the filesystem). Confirmed by running
journalctl --boot
And seeing that the logs for the current boot have bad timestamps. Adding this in case anyone else’s problem happens to match mine.
Method 5
Quick way to replace Sudo Cron and Cron timers
I followed a tutorial to create a .timer and .service unit file and created five for a range of tasks including sudo rsync, rsnapshot, local copies and calling scripts. It worked well except that they also ran at startup. I spent half a day following different ideas that didn’t fix it.
Advice included removing requireds, wanted bys, and using User =.
@TLDR Despite system restarts the files ran at startup until I deleted the old files, changed to new names, and started fresh. So here’s what worked.
@TLDR Create two simple files in /etc/systemd/system/ with the same name but suffixes service and timer. Just the fields shown below but your own exec commands. Then enable and start the timer only before restarting your system and checking that a) they didn’t run on startup and b) they are in the timer list with the correct next time.
So, here are two files that just echo a message so you can check your log to find out what happened and when. Always use the same base name for each pair to simplify the code. These are test.service and test.timer.
[Unit] Description=This is test.service [Service] Type=oneshot ExecStart=/bin/echo "**** I ran at this now ****"
[Unit] Description=This is test.timer [Timer] OnCalendar=*-*-* 10:45:00 [Install] WantedBy=timers.target
Create the files then run sudo systemctl enable test.timer and sudo systemctl start test.timer. Then restart your system.
The status and journalctl commands below can be run for both .timer and
.service to confirm that they acted as you expected.
My permissions & ids were all honoured so the issues suggested in some threads about using User= seem irrelevant. And both sudo crontab and crontab commands ran fine from systemd. Enjoy.
Other useful commands:
systemctl list-unit-files --state=enabled # whats live
systemctl list-timers # should now include yours
systemctl status test.timer # check the timers enabled
systemctl status test.service # and the service isn’t
sudo journalctl -u test.timer # check the timer ran at startup
sudo journalctl -u test.service # but the service didn’t
https://man.archlinux.org/man/systemd.time.7 # time formats
Method 6
There are a couple more possibilities not covered in other answers…
Possibility A
It is possible that on reboot it is not the timer that is starting the service but rather that the service itself was installed/enabled (ie via systemctl enable <service-unit>) at some point previously.
For instance service starting because you had an [Install] section in your service unit and enabled the service at some point in the past.
Possibility B
The service could be running because it was declared as a dependency of some other unit.
There is a top search result blog post about cron to systemd timers that has examples where the Timer does a Requires=myUnit.service. When the Timer is started at boot (as it should)…because the Timer requires the service (a dependency) the Service is started before the Timer…ie on every boot.
To check…
- Do a reboot,
- Did the service just run at reboot?
systemctl status <service-unit>- If no, you don’t have a problem, done.
- Was it the timer that ran the service at boot?:
systemctl list-timers- check the timer for your service. Did it just run/trigger?
- if yes: then the timer triggered the service run.
- check
Persistentsetting in timer unit – maybe ran because Timer thought it missed a run.
- check
- if no:
- make sure the timer unit doesn’t have a dependency on your service (
Requires=<service-unit>or something dumb like that) - can
grep -R <service-unit> /etc/systemd/system/to see if other units are referencing or depending on your service. - If your timer (and other units) aren’t depending on your service…then likely the service ran due to previously being installed/enabled.
- make sure the timer unit doesn’t have a dependency on your service (
- Run
systemctl is-enabled <service-unit>. If it saysstaticthat means it is not enabled and has no[Install]section (good).- Idiot check this by running
find /etc/systemd/system/*.wants -name '<service-name>*'and making sure there are no left-over symlinks floating around from a previous install/enable that would start the service.
- Idiot check this by running
- If the service is enabled for some reason…
- Run
systemctl disable <service-unit> - Remove the
[Install]section from the service unit - run
systemctl --system daemon-reloadto load your changes. - Reboot and confirm the service (hopefully) no longer is running at boot.
- Run
Tips for using timers as cron replacement…
- You don’t want your timer to
Requires=(etc) your service. - You don’t want your service to
Wants=orRequires=(etc) your timer. - You should include an
[Install]section in your timer unit so it is started with the rest of the timers on boot. - You want to enable/start your timer so it is running and will start running again after reboot. (note timer running doesn’t mean service running).
- You do NOT want to
enableyour service and should NOT include an[Install]section in your service unit.
I think many of the “solutions” here worked because people re-created the unit files in a new location/name without an [Install] section and/or without running systemctl enable <service-unit>. This works but is not necessary. You can just run systemctl disable <service-unit> on the original service unit without needing to re-create.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0