With systemd, you do not simply make Units depend on each other. You separately design which Units should be started together and which one should start first.
The following differences are especially important.
| Setting | Main role |
|---|---|
Wants= | Adds the dependency to the set of Units to start. A failure of the dependency is handled relatively loosely |
Requires= | Adds the dependency to the set of Units to start and treats it as a stronger mandatory relationship |
After= | Starts the current Unit after the specified Unit |
Before= | Starts the current Unit before the specified Unit |
.target | Groups multiple Units logically |
Wants= and Requires= define requirement relationships, while After= and Before= define ordering relationships. For example, specifying only Wants=example.service does not mean that the current Unit will start after example.service has finished. If ordering is also required, add a directive such as After=.
In this article, we create oneshot services for verification on an RHEL 8-compatible Linux system and check their behavior both when dependencies work normally and when they are intentionally made to fail.
Step 1: Create Units for verification
Section titled “Step 1: Create Units for verification”First, create Units for checking dependencies, startup order, and targets.
example-fail.service is a dependency that intentionally fails. By referencing the same Unit from both example-wants.service and example-requires.service, we can compare the difference between Wants= and Requires=.
For startup-order verification, we use example-order-base.service, example-after.service, and example-before.service.
In ExecStart= within a Unit file, % is interpreted by systemd as a specifier. Therefore, when passing %s as a printf format specifier, write it as %%s in the Unit file.
sudo tee /etc/systemd/system/example-fail.service >/dev/null <<'EOF'[Unit]Description=Example intentionally failing service
[Service]Type=oneshotExecStart=/usr/bin/falseRemainAfterExit=yesEOF
sudo tee /etc/systemd/system/example-wants.service >/dev/null <<'EOF'[Unit]Description=Example service using WantsWants=example-fail.serviceAfter=example-fail.service
[Service]Type=oneshotExecStart=/usr/bin/bash -c 'printf "%%s\n" "wants-parent-started" > /var/tmp/example-wants.result'RemainAfterExit=yesEOF
sudo tee /etc/systemd/system/example-requires.service >/dev/null <<'EOF'[Unit]Description=Example service using RequiresRequires=example-fail.serviceAfter=example-fail.service
[Service]Type=oneshotExecStart=/usr/bin/bash -c 'printf "%%s\n" "requires-parent-started" > /var/tmp/example-requires.result'RemainAfterExit=yesEOF
sudo tee /etc/systemd/system/example-order-base.service >/dev/null <<'EOF'[Unit]Description=Example ordering base service
[Service]Type=oneshotExecStart=/usr/bin/bash -c 'printf "%%s\n" "base" >> /var/tmp/example-order.log'RemainAfterExit=yesEOF
sudo tee /etc/systemd/system/example-after.service >/dev/null <<'EOF'[Unit]Description=Example service ordered after baseWants=example-order-base.serviceAfter=example-order-base.service
[Service]Type=oneshotExecStart=/usr/bin/bash -c 'printf "%%s\n" "after" >> /var/tmp/example-order.log'RemainAfterExit=yesEOF
sudo tee /etc/systemd/system/example-before.service >/dev/null <<'EOF'[Unit]Description=Example service ordered before baseWants=example-order-base.serviceBefore=example-order-base.service
[Service]Type=oneshotExecStart=/usr/bin/bash -c 'printf "%%s\n" "before" >> /var/tmp/example-order.log'RemainAfterExit=yesEOF
sudo tee /etc/systemd/system/example-unordered.service >/dev/null <<'EOF'[Unit]Description=Example service with dependency but no explicit orderingWants=example-order-base.service
[Service]Type=oneshotExecStart=/usr/bin/trueRemainAfterExit=yesEOF
sudo tee /etc/systemd/system/example-target-a.service >/dev/null <<'EOF'[Unit]Description=Example target member A
[Service]Type=oneshotExecStart=/usr/bin/bash -c 'printf "%%s\n" "target-a-started" > /var/tmp/example-target-a.result'RemainAfterExit=yesEOF
sudo tee /etc/systemd/system/example-target-b.service >/dev/null <<'EOF'[Unit]Description=Example target member B
[Service]Type=oneshotExecStart=/usr/bin/bash -c 'printf "%%s\n" "target-b-started" > /var/tmp/example-target-b.result'RemainAfterExit=yesEOF
sudo tee /etc/systemd/system/example-stack.target >/dev/null <<'EOF'[Unit]Description=Example grouped servicesWants=example-target-a.service example-target-b.serviceEOF
sudo systemctl daemon-reloadAfter modifying Unit files, use systemctl daemon-reload to make the systemd manager reload them.
Step 2: Check Unit file syntax
Section titled “Step 2: Check Unit file syntax”Before running startup tests, inspect the Unit files with systemd-analyze verify.
sudo systemd-analyze verify \ /etc/systemd/system/example-fail.service \ /etc/systemd/system/example-wants.service \ /etc/systemd/system/example-requires.service \ /etc/systemd/system/example-order-base.service \ /etc/systemd/system/example-after.service \ /etc/systemd/system/example-before.service \ /etc/systemd/system/example-unordered.service \ /etc/systemd/system/example-target-a.service \ /etc/systemd/system/example-target-b.service \ /etc/systemd/system/example-stack.targetIf errors are displayed, correct the Unit file definitions before testing the dependencies.
Step 3: Check the dependencies recognized by systemd
Section titled “Step 3: Check the dependencies recognized by systemd”Rather than looking only at what is written in the files, check the dependencies that systemd actually recognizes after reloading them.
sudo systemctl show example-wants.service example-requires.service example-after.service example-before.service example-unordered.service \ -p Id -p Wants -p Requires -p After -p Before --no-pager
sudo systemctl show example-wants.service -p Wants --value | grep -qw 'example-fail.service'sudo systemctl show example-wants.service -p After --value | grep -qw 'example-fail.service'sudo systemctl show example-requires.service -p Requires --value | grep -qw 'example-fail.service'sudo systemctl show example-requires.service -p After --value | grep -qw 'example-fail.service'sudo systemctl show example-after.service -p After --value | grep -qw 'example-order-base.service'sudo systemctl show example-before.service -p Before --value | grep -qw 'example-order-base.service'Here, verify that Wants= and After= are recognized for example-wants.service, and that Requires= and After= are recognized for example-requires.service.
Step 4: Check behavior when a Wants dependency fails
Section titled “Step 4: Check behavior when a Wants dependency fails”First, test the configuration that uses Wants=.
The dependency example-fail.service always fails, but example-wants.service is configured so that its own startup processing can still run after that failure.
sudo systemctl stop example-wants.service example-fail.servicesudo systemctl reset-failed example-wants.service example-fail.servicesudo rm -f /var/tmp/example-wants.resultsudo systemctl start example-wants.serviceCheck the state of the requesting Unit and the dependency using properties maintained by systemd itself.
sudo test "$(sudo systemctl show example-wants.service -p ActiveState --value)" = "active"sudo test "$(sudo systemctl show example-wants.service -p Result --value)" = "success"sudo test "$(sudo systemctl show example-fail.service -p ActiveState --value)" = "failed"sudo test "$(sudo systemctl show example-fail.service -p Result --value)" = "exit-code"
sudo systemctl show example-wants.service example-fail.service \ -p Id -p ActiveState -p SubState -p Result --no-pagerIf example-fail.service is failed while example-wants.service is active with Result=success, this confirms that failure of a dependency added to the startup set with Wants= does not necessarily prevent the requesting Unit from starting successfully.
Because After=example-fail.service is also specified here, processing of the requesting Unit proceeds after the dependency’s startup job has completed.
Step 5: Check behavior when a Requires dependency fails
Section titled “Step 5: Check behavior when a Requires dependency fails”Next, reference the same failing Unit with Requires= and After=.
sudo systemctl stop example-requires.service example-fail.servicesudo systemctl reset-failed example-requires.service example-fail.servicesudo rm -f /var/tmp/example-requires.resultsudo systemctl start example-requires.serviceThis startup is expected to fail because the dependency fails.
Also verify that the requesting Unit’s processing did not run.
sudo systemctl show example-requires.service example-fail.service \ -p Id -p ActiveState -p SubState -p Result --no-pagersudo test ! -e /var/tmp/example-requires.resultRequires= alone does not define startup order. Because After=example-fail.service is also specified in this example, the mandatory dependency attempts to start first and fails, and that failure is then reflected in the startup of the requesting Unit.
This is why requirement relationships and ordering relationships need to be considered separately.
Step 6: Specify startup order with After
Section titled “Step 6: Specify startup order with After”Now verify the ordering defined by After=.
example-after.service adds example-order-base.service to the same startup transaction with Wants=, and additionally uses After= to specify that it should begin only after startup of the base Unit has completed.
sudo systemctl stop example-after.service example-order-base.servicesudo rm -f /var/tmp/example-order.logsudo systemctl start example-after.serviceCompare the monotonic timestamps recorded by systemd. Confirm that the main process of the after Unit did not start before the base Unit transitioned to active.
base_active="$(sudo systemctl show example-order-base.service -p ActiveEnterTimestampMonotonic --value)"after_start="$(sudo systemctl show example-after.service -p ExecMainStartTimestampMonotonic --value)"
sudo test -n "$base_active"sudo test -n "$after_start"sudo test "$base_active" -le "$after_start"
sudo systemctl show example-order-base.service example-after.service \ -p Id -p ActiveState -p ActiveEnterTimestampMonotonic -p ExecMainStartTimestampMonotonic --no-pagerIf base_active is less than or equal to after_start, this confirms that execution of example-after.service began after example-order-base.service entered its completed startup state.
After= defines which Unit is processed first when the other Unit is also part of the startup operation. In this example, Wants= adds the dependency to the startup set, and After= determines the order between the two.
Step 7: Specify the reverse startup order with Before
Section titled “Step 7: Specify the reverse startup order with Before”Next, specify Before= relative to the same base Unit.
sudo systemctl stop example-before.service example-order-base.servicesudo rm -f /var/tmp/example-order.logsudo systemctl start example-before.serviceVerify the Before= ordering using the monotonic timestamps recorded by systemd. Confirm that the main process of the base Unit starts only after example-before.service has transitioned to active.
before_active="$(sudo systemctl show example-before.service -p ActiveEnterTimestampMonotonic --value)"base_start="$(sudo systemctl show example-order-base.service -p ExecMainStartTimestampMonotonic --value)"
sudo test -n "$before_active"sudo test -n "$base_start"sudo test "$before_active" -le "$base_start"
sudo systemctl show example-before.service example-order-base.service \ -p Id -p ActiveState -p ActiveEnterTimestampMonotonic -p ExecMainStartTimestampMonotonic --no-pagerIf before_active is less than or equal to base_start, this confirms that execution of example-order-base.service began after startup processing of example-before.service had completed.
Before= represents an ordering relationship in the opposite direction from After=.
When reading dependencies, if Unit A contains Before=B, it is useful to interpret that as “place A before B.”
Step 8: Verify that Wants alone does not specify startup order
Section titled “Step 8: Verify that Wants alone does not specify startup order”example-unordered.service has only the following dependency configured.
Wants=example-order-base.serviceNeither After= nor Before= is specified.
Start both Units and check the relationship recognized by systemd.
sudo systemctl stop example-unordered.service example-order-base.servicesudo rm -f /var/tmp/example-order.logsudo systemctl start example-unordered.servicesudo systemctl is-active --quiet example-unordered.servicesudo systemctl is-active --quiet example-order-base.service
sudo systemctl show example-unordered.service -p Wants -p After -p Before --no-pagersudo systemctl show example-unordered.service -p Wants --value | grep -qw 'example-order-base.service'
if sudo systemctl show example-unordered.service -p After --value | grep -qw 'example-order-base.service'; then exit 1fi
if sudo systemctl show example-unordered.service -p Before --value | grep -qw 'example-order-base.service'; then exit 1fiIn this state, both Units are included in the startup set, but there is no explicit After= or Before= relationship between example-unordered.service and example-order-base.service.
Therefore, it is incorrect to use Wants= as a setting meaning “start the dependency first.” If ordering is required, design After= or Before= separately from the requirement relationship.
Step 9: Group multiple Units with a target
Section titled “Step 9: Group multiple Units with a target”When multiple related services need to be started together, a .target Unit can be used as a grouping unit.
In this example, example-stack.target includes the following two services with Wants=.
example-target-a.serviceexample-target-b.service
Start only the target and verify that both services are started.
sudo systemctl stop example-stack.target example-target-a.service example-target-b.servicesudo rm -f /var/tmp/example-target-a.result /var/tmp/example-target-b.resultsudo systemctl start example-stack.target
sudo systemctl is-active --quiet example-stack.targetsudo systemctl is-active --quiet example-target-a.servicesudo systemctl is-active --quiet example-target-b.servicesudo test -f /var/tmp/example-target-a.resultsudo test -f /var/tmp/example-target-b.resultsudo cat /var/tmp/example-target-a.resultsudo cat /var/tmp/example-target-b.resultIf both target-a-started and target-b-started can be confirmed, the configuration that starts multiple Units together from a single target is working.
Design considerations
Section titled “Design considerations”With systemd dependencies, it is important to separate the questions “should this Unit be started?” and “when should this Unit be started?”
Typically, if you want a dependency to start together with the requesting Unit but do not want its failure to directly cause the requesting Unit’s startup to fail, consider a combination such as the following.
Wants=example.serviceAfter=example.serviceIf successful startup of the dependency is required as a condition for starting the requesting Unit, combine a stronger requirement relationship with an ordering relationship.
Requires=example.serviceAfter=example.serviceOn the other hand, if you only want to specify the order, you can also use only After= or Before= without adding a requirement relationship. In that case, the ordering directive alone does not automatically start the other Unit. Both Units must become part of the startup operation through another dependency or mechanism.
When multiple Units should be treated as a single functional group, using a target can express the intended configuration more clearly than an operational procedure that directly starts individual services one after another.
The basic principle for keeping systemd Unit configurations predictable is not to treat Wants=, Requires=, After=, and Before= as the same kind of “dependency setting,” but to design requirement relationships and ordering relationships separately.