47

I would like to execute a script every 30 min after booting into the system. I know you can use cron, but I don't plan to use this feature often therefore I'd like to try it with systemd.

So far I have only found the monotonic timers which allows to execute something once (at least I think so). How would the foo.timer and foo@user.service look like in case I wanted to execute something every 30 minutes from boot/system start?

foo@user.service

[Unit]
Description=run foo
Wants=foo.timer

[Service]
User=%I
Type=simple
ExecStart=/bin/bash /home/user/script.sh

foo.timer

[Unit]
Description=run foo

[Timer]
where I am stuck... ???
TomTom
  • 2,603
  • 6
  • 18
  • 23

3 Answers3

59

You need to create two files: one for service, other for timer with same name.

example:

/etc/systemd/system/test.service

[Unit]
Description=test job

[Service] Type=oneshot ExecStart=/bin/bash /tmp/1.sh

/etc/systemd/system/test.timer

[Unit]
Description=test

[Timer] OnUnitActiveSec=10s OnBootSec=10s

[Install] WantedBy=timers.target

after that reload the systemd using command systemctl daemon-reload and start your timer by systemctl start test.timer, or enable it by default (systemctl enable test.timer).

test content of 1.sh

#!/bin/bash
echo `date` >> /tmp/2

And command to check all available timers: systemctl list-timers --all

More detailed info on project page and examples on ArchLinux page

Reishin
  • 746
  • systemd accepts the scripts and it is listed, however nothing is happening – TomTom Apr 24 '15 at 19:12
  • which one? there should be two scripts, one timer and another service. Time, when they was executed could be checked by list-timers command, Possible errors could be checked by systemctl status test.timer and systemctl status test.service comamand – Reishin Apr 24 '15 at 19:19
  • I added both scripts and reloaded the daemon. Then started test.timer. The status tells me that it is active (running) however nothing is happening. – TomTom Apr 24 '15 at 19:24
  • 2
    please use systemctl list-timers --all command and check the output. He need to be like this. Look to unit, left and passed columns. If timer is present, please look to your service file and check for bugs there since timer working normally. – Reishin Apr 24 '15 at 19:30
  • Alright I figured it out.. you need /bin/bash script.sh as well as making the script executable. But unfortunately it doesn't trigger notify-send which creates notifications in xfce4 – TomTom Apr 24 '15 at 19:46
  • hah, this is another case. the problem is how notify-send works. utility send command through D-BUS, since in most cases notify services use session D-BUS, when you would run command outside of your desktop session - nothing will happen. You should save somewhere $DBUS_SESSION_BUS_ADDRESS variable from your desktop session and export saved variable in your systemd script. And sure, you need run script under same user as your desktop one. – Reishin Apr 24 '15 at 20:02
  • I thought so! Do you mind extending your answer? – TomTom Apr 24 '15 at 20:08
  • 2
    no, coz main question doesn't ask nothing about "notify-send" and i think we should not mix two different things when such topic already present. in your case, try to add export DISPLAY=:0.0 to the script. – Reishin Apr 24 '15 at 20:26
  • 1
    ps: according to man systemd.timer the Persistent=true only has an effect on configured with OnCalendar (i.e. wallclock) – snyh Jul 28 '16 at 02:58
  • @snyh thx, and looks like OnUnitActiveSec is related to monotonic timers so we can safely remove Persistent=true which belongs to OnCalendar :) Will edit post – Reishin Jul 28 '16 at 18:18
  • 3
    Where is 30min set in your example? – Karl Morrison Aug 08 '17 at 14:45
  • 3
    @KarlMorrison it is set to 10s in example, change value to 30min for full satisfaction expirience – Reishin Aug 08 '17 at 15:29
  • 1
    I am missing something here. How does the timer know that the test.service is to be run? – Dagelf Apr 21 '22 at 05:44
  • 1
    @Dagelf [unit name].[type of service] i.e. we have unit name test with service types service and timer. What would be triggered available as well via systemctl status test.timer. So answering bluntly, by filename before dot ... – Reishin Apr 21 '22 at 22:13
  • .timer will never fire with only OnUnitActiveSec= when its Unit=.service is Type=oneshot: https://github.com/systemd/systemd/issues/6680 – n0099 Feb 23 '24 at 12:12
24

Here is another option without using your timer. If timing is not terribly critical and and script isn't long running it will be fine for simple things.

[Unit]
Description=Run foo

[Service]
User=%I
Restart=always
RestartSec=1800s
ExecStart=/bin/bash /home/user/script.sh
hookenz
  • 1,277
  • 6
    I like this solution. The only major drawback is that system logs will be flooded with "Starting " logs if you restart often (i.e. every 30 seconds). At which point it might be better to run the service as a daemon instead, instead of restarting the service over and over from systemd. – jersey bean Aug 05 '17 at 00:36
  • That's true. For quick and simple stuff it works. But the timer or a long running script would be a better solution. – hookenz Aug 05 '17 at 03:09
10

The proper way is to use systemd-run you can schedule your job without the need to define your own unit.

It allows you to schedule by a calendar or every period of time. Assuming MYSELF is your full path application:

systemd-run --user --on-calendar '*:0/1' ${MYSELF} <args>
fcm
  • 447
  • Does this run "every 30 minutes from boot/system start"? How would the OP adapt it to do that? – Jeff Schaller Dec 01 '20 at 02:38
  • 1
    @JeffSchaller I'm about to try this out, but it looks like OP's requirement could be met with systemd-run --on-boot=1800 --on-unit-active=1800 /home/user/script.sh – Tony Park Nov 18 '21 at 14:40
  • 1
    This should be the accepted answer, as op stated he wants to run a script, not explicitly create a service and a timer. systemd-run does that in the background without the user noticing. – m.w. Sep 15 '22 at 09:59