5

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.

  1. I disabled all of my .service and .timer files, and moved them out of my home directory into /etc/systemd/system.

  2. 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.

2
  • 1
    Please post your solution as an answer and accept it, that was the question is marked as solved and others can easily find the answer.
    – terdon
    May 18, 2017 at 8:25
  • 1
    Since you never answered my comment, I posted your solution as an answer myself. If you decide you want to post it yourself and reap the reputation you deserve for it, let me know and I'll delete mine.
    – terdon
    May 22, 2017 at 11:15

7 Answers 7

10

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.

5
  • 1
    Better start with the TLDR as knowing that makes it easier to understand the rest of your answer :) May 28, 2020 at 11:58
  • If you have a service that currently runs at startup and you don't want it to anymore, remove the [Install] section in the service file, then run systemctl --user daemon-reload to update your services and systemctl --user disable <unit> to remove the symlink that's called at startup. Oct 7, 2020 at 14:53
  • This should be the accepted answer.
    – ceremcem
    Feb 3, 2021 at 20:38
  • This was my problem as well Nov 1, 2021 at 3:17
  • So what kind of target should one use, in order to avoid having its service executed on boot. e.g. should we use WantedBy=multi-user.target1? should we NOT use an [Install] section at all?
    – pkaramol
    Nov 12, 2021 at 19:22
2

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 Persistent setting in timer unit - maybe ran because Timer thought it missed a run.
    • 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.
  • Run systemctl is-enabled <service-unit>. If it says static that 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.
  • 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-reload to load your changes.
    • Reboot and confirm the service (hopefully) no longer is running at boot.

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= or Requires= (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 enable your 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.

1
  • For me it was the Requires= AND Unit= both present in the timer, thanks a ton!
    – Tooster
    Feb 7 at 18:43
1

According documentation you should change your config to Persistent=false or remove Persistent at all, because it false by default.

2
  • Sorry, the example file I showed isn't representative of the real unit files I am having problems with. They have Persistent=false, so that isn't the problem. I will edit that line out.
    – bitofagoob
    May 9, 2017 at 22:11
  • @bitofagoob try to remove Install section from .timer file. May 10, 2017 at 8:53
0

I resolved this problem by converting my 'user' timers into root/system timers.

  1. I disabled all of my .service and .timer files, and moved them out of my home directory into /etc/systemd/system.

  2. 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.

0

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.

0

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

-1

If you require some service on your timer file, remove it, because systemd runs whats inside of require. source

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .