44

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 [email protected] look like in case I wanted to execute something every 30 minutes from boot/system start?

[email protected]

[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,453
  • 6
  • 18
  • 23

3 Answers3

54

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
  • 686
  • 4
  • 8
  • 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](http://pastebin.com/cteCmFGw). 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](http://unix.stackexchange.com/questions/111188/using-notify-send-with-cron) 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
  • 2
    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
  • 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
23

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,207
  • 1
  • 15
  • 19
  • 5
    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
9

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
  • 427
  • 4
  • 15
  • 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