Lev Dubinets
Lev Dubinets

Reputation: 808

Systemd timer for a bound service restarts the service it is bound to

I have two systemd services a and b, where b is "After" and "BindsTo" a, and b is a short command that is launched every minute with a systemd timer.

Heres my config:

$ cat /systemd/a.service
[Unit]
After=foo
BindsTo=foo

[Service]
ExecStart=/opt/a/bin/a
Group=lev
User=lev
Restart=Always
WorkingDirectory=/opt/a

$ cat /systemd/b.service
[Unit]
After=a
BindsTo=a

[Service]
ExecStart=/opt/b/bin/b
Group=lev
User=lev
WorkingDirectory=/opt/b

$ cat /systemd/b.timer
[Unit]

[Timer]
OnCalendar=*:0/1:00

When I run sudo systemctl stop a, service a is indeed stopped, but then it is started back up at the top of the next minute when the timer for service b runs b

The systemd documentation states that BindsTo

declares that if the unit bound to is stopped, this unit will be stopped too.

(https://www.freedesktop.org/software/systemd/man/systemd.unit.html#BindsTo=)

I expect that by stopping a, b will also be stopped, and the timer disabled. This is not the case. Can you help explain why the b timer restarts not only b (which should fail), but also a?

Can you also help me edit these services such that:

Thanks in advance!

Upvotes: 3

Views: 2003

Answers (2)

duthils
duthils

Reputation: 1391

Here are the simplest units that meet your constraints:

test-a.service

[Service]              
ExecStart=sleep 3600  # long-running command

test-b.service

[Service]     
ExecStart=date  # short command

test-b.timer

[Unit]                                
After=test-a.service
BindsTo=test-a.service   # makes test-b.timer stop when test-a.service stops
                                      
[Timer]                                           
OnCalendar=* *-*-* *:*:00             
                                      
[Install]                             
WantedBy=test-a.service  # makes test-b.timer start when test-a.service starts

Don't forget to

  • systemctl daemon-reload
  • systemctl disable test-b.timer
  • systemctl enable test-b.timer To apply the changes in the [Install] section.

Explanations:

  • what you want is to bind a.service with b.timer, not b.service
  • b.service is only a short command, and systemctl start b.service will only run the command, not start the associated timer
  • only systemctl start b.timer will start the timer
  • The WantedBy tells systemd to start test-b.timer when test-a.service starts
  • The BindsTo tells test-b.timer to stop when test-a.service stops
  • The After only ensures that test-b.timer is not started at the same time than test-a.service: it will force systemd to start test-b.timer after test-a.service has finished starting.

About the behaviour you observed:

  • When you stopped your a.service, the b.timer was still active and it tried starting b.service to run its short command. Since your b.service specified BindsTo=a.service, systemd thought that b.service required a.service to be started also, and effectively restarted a.service for b.service to run correctly.

Upvotes: 1

ottokram
ottokram

Reputation: 1

I could be mistaken, but I believe that the "Restart=Always" option is the reason that the service named a is started and hence why the service named b is not subsequently stopped.

The man page for systemd.service states if this option is set to always

the service will be restarted regardless of whether it exited cleanly or not, got terminated abnormally by a signal, or hit a timeout.

https://www.freedesktop.org/software/systemd/man/systemd.service.html#Restart=

So even though you are stopping the service, this option is starting it again.

You can test this by running the following commands. Since you have service "b" on a one minute timer, I would run the stop command at 10 seconds after the top of the minute (i.e. 10:00:10). Then I would run the status command 20 seconds later and see if the service has been restarted.

sudo systemctl stop a sudo systemctl status a b

Upvotes: -1

Related Questions