In systemd, what's the difference between After= and Requires=?

Solution 1:

After= configures service order (do X only after Y), while Requires= 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 the After= line isn't started at all, while Requires= would prevent it's start if the requirement isn't met.

Citing https://www.freedesktop.org/software/systemd/man/systemd.unit.html:

Requires=

Configures requirement dependencies on other units. If this unit gets activated, the units listed here will be activated as well. If one of the other units gets deactivated or its activation fails, this unit will be deactivated. This option may be specified more than once or multiple space-separated units may be specified in one option in which case requirement dependencies for all listed names will be created. Note that requirement dependencies do not influence the order in which services are started or stopped. This has to be configured independently with the After= or Before= options. If a unit foo.service requires a unit bar.service as configured with Requires= and no ordering is configured with After= or Before=, then both units will be started simultaneously and without any delay between them if foo.service is activated. Often, it is a better choice to use Wants= instead of Requires= in order to achieve a system that is more robust when dealing with failing services.

and

Before=, After=

A space-separated list of unit names. Configures ordering dependencies between units. If a unit foo.service contains a setting Before=bar.service and both units are being started, bar.service's start-up is delayed until foo.service is started up. Note that this setting is independent of and orthogonal to the requirement dependencies as configured by Requires=. It is a common pattern to include a unit name in both the After= and Requires= option, in which case the unit listed will be started before the unit that is configured with these options. This option may be specified more than once, in which case ordering dependencies for all listed names are created. After= is the inverse of Before=, i.e. while After= ensures that the configured unit is started after the listed unit finished starting up, Before= ensures the opposite, i.e. that the configured unit is fully started up before the listed unit is started. Note that when two units with an ordering dependency between them are shut down, the inverse of the start-up order is applied. i.e. if a unit is configured with After= on another unit, the former is stopped before the latter if both are shut down. Given two units with any ordering dependency between them, if one unit is shut down and the other is started up, the shutdown is ordered before the start-up. It doesn't matter if the ordering dependency is After= or Before=. It also doesn't matter which of the two is shut down, as long as one is shut down and the other is started up. The shutdown is ordered before the start-up in all cases. If two units have no ordering dependencies between them, they are shut down or started up simultaneously, and no ordering takes place.

Solution 2:

One of the major difference is,

  • After only checks if the unit is activated already, and does not explicitly activate the specified units.
  • The units listed in 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,

[Unit]
Description=test app
After=network-online.target

Here is what will happen when this statement is executed,

  • After checks if network-online.target.
  • if network-online.target not started, it will wait.
  • test-app starts only after network-online.target is active

If I had Requires instead,

[Unit]
Description=test app
Requires=network-online.target

Here is what will happen when this statement is executed,

  • network-online.target and test-app are activated together
  • if network-online.target fails to start test-app will not be activated.

Solution 3:

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

Tags:

Systemd