(systemd version 229, fwiw)
I have a primary service A, and a secondary service B. The primary A can run by itself. But service B cannot run correctly by itself: it needs A to be running (technically B can run, but this is what I want systemd to prevent). My goal: If A is not running, B should not run. Given that A and B are running, when A stops or dies/crashes, then B should be stopped.
How do I achieve this?
I get close by adding [Unit] items to b.service, using
Requisite=A.service
After=A.service
The result of the above is that
1) B won't start unless A is running (good).
2) B is stopped when A is stopped (good).
3) However, if I kill A, service B continues to run (bad).
How can I fix this last behavior #3? I tried using BindsTo instead of Requisite, like this in B's service file:
BindsTo=A.service
After=A.service
and I get:
1) If A is not running and I start B, then A is also started
(I want an error, I don't want A started)
2) B is stopped when A is stopped (good).
3) B is stopped when A is killed (good)
So now #3 is good but #1 is not the desired behaviour. Neither PartOf nor BindsTo seems to do the trick, but perhaps I don't have the right incantation of combinations of options? Its not clear to me from the man pages what options can be combined.
With BindsTo, I also tried failing B's start using ExecStartPre, but of course this didn't work because the start of B had already determined that it needed A to run (and started it), before it fired up B.
Is there some way to get a behaviour inbetween Requisite and BindsTo? The 1-2-3 I've listed above?
Thanx in advance!
I'm also looking for a more elegant solution. There is an open systemd ticket for Request for enhancement here: https://github.com/systemd/systemd/issues/5966
Currently my workaround is as follow: B.service
A.service
Stop-B.service
I had a similar situation to solve. The approach I came up with involve creating a target unit as barrier between the two services.
a.service:
a.target:
b.service:
This setup produces the behavior the OP desires, since a.target is always active with a.service (PartOf stops a.target when a.service is stopped, RefuseManual* avoid systemctl start/stop commands on a.target), and b.service cannot start a.service via a.target.
I went with a similar approach to Chris's, but without needing to create a separate service for stopping B:
B.service:
A.service:
The
Requisite
line in B.service ensures that B.service cannot be started if A.service is not already running, while theExecStopPost
line in A.service ensures that B.service gets stopped when A.service is stopped or goes inactive.