Search code examples
servicesystemdsystem-administration

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:

  • on boot, a is started first, then b is started
  • when I sudo systemctl stop a, b's timer does not run
  • when I sudo systemctl start a, b's timer begins running again

Thanks in advance!


Solution

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