I'm creating a systemd .service file and I need help understanding the difference between Requires=
and After=
. The man page says that Requires=
"Configures requirement dependencies on other units." and After=
"Configures ordering dependencies between units." What's the difference?
After=
configures service order (do X only after Y), whileRequires=
state dependencies. If you don't specify an order, a service depending on another would be started at the same time as the one it is depending on. Also, the way I understand it (although I can't test that now and don't find a reference),After=
is a "loose coupling", and a service with such a statement would still run if the one named in theAfter=
line isn't started at all, whileRequires=
would prevent it's start if the requirement isn't met.Citing https://www.freedesktop.org/software/systemd/man/systemd.unit.html:
and
One of the major difference is,
After
only checks if the unit is activated already, and does not explicitly activate the specified units.Requires
are activated together with the unit. If any of the required units fail to start, the unit is not activated.Consider I have a unit file
test-app.service
,Here is what will happen when this statement is executed,
After
checks ifnetwork-online.target
.network-online.target
not started, it will wait.test-app
starts only afternetwork-online.target
is activeIf I had
Requires
instead,Here is what will happen when this statement is executed,
network-online.target
andtest-app
are activated togethernetwork-online.target
fails to starttest-app
will not be activated.systemd is a job manager. The man page is not very precise as to how things work.
When you boot, what systemd does is build a transaction comprising of jobs for the anchor job (i.e. start job for default.target). What all these dependencies and relationships do is define how and what jobs will be triggered. Ordering defines what job(s) every other job will wait on. The default.target unit hence is at the centre of all this, which is why when enabling units you use a reverse dependency which through systemctl enable creates a filesystem symbolic link denoting a forward dependency systemd can follow (also why you need filesystem symlinks in the first place). Similar is when you manually start some unit, then that unit is anchor, and transaction is computed wrt it.
Not going in too much detail, I'll explain what Requires= and After= does.
Requires= will cause systemd to trigger a start job for the required unit when you get a start job triggered (explicitly, or through a dependency: there is no distinction internally). It also has the property of triggering a stop job on you when this unit is stopped (note: stopped, not going down on its own) or restarted. This means that if some dependency/systemctl causes it to stop/restart, you will also stop/restart. However, if it goes down on its own, you will not stop, as there was no job, and the state change happened without systemd's involvement. That's where you would use BindsTo= (similar to device units, which can go to inactive without systemd's involvement, for obvious reasons).
Now, the use of After= is recommended as Requires= alone is racy for what it does: cancel the requiree if the start job fails. This cancellation however only works wrt jobs, i.e. if the other unit does not define ordering, systemd triggers both in parallel, and if its start job finishes before your start job fails, it will not be cancelled (it cannot be cancelled, in fact). Use of After= means that other job keeps waiting until the start job of the required unit finishes, and depending on the result, if it failed, your unit's waiting start job is cancelled with the JOB_DEPENDENCY job result (why you use yellow [DEPEND] at boot for such cases). Hence, this invalidation effect is undeterministic without use of After=.
This is why using Wants= without After= is fine if you don't want to wait for the startup of the other unit: as there is no invalidation there, so there is no race. In that case, it is no more than a synchronization mechanism.
Also, you can also enable both at boot, and not require one another, and only define ordering, in that case, when both are pulled as part of the same transaction, they will be ordered (or if the job for the other is triggered while the job for the unit it wants to run after is running, it will first wait for it finish, across transactions).
Now if there is no job, ordering has no effect for the said unit. However, there usually is a job, as a consequence of use of dependencies like Requires= and Wants=, or both getting pulled in at a time and define some ordering, in which case they do wait on the another unit's job(s).