Right, let’s talk about dependencies. This is where you stop just running services and start orchestrating them. It’s the difference between a bunch of people milling about in a room and a well-rehearsed play where everyone knows their cues, their entrances, and their exits. systemd’s dependency system is how you write that play.

The core idea is simple: you tell systemd about the relationships between your units (services, sockets, timers, etc.). “Don’t start this until that is running,” or “If this thing dies, take that thing down with it.” It feels like it should be straightforward, but of course, it’s not. The designers gave us a few too many knobs, and some of them do surprisingly subtle things.

The Core Dependency Types: Wants vs. Requires

Let’s get the big two out of the way first: Wants and Requires. Both are directives you stick in the [Unit] section of a unit file to say, “Hey, I need this other unit.”

Requires is your high-maintenance, all-or-nothing friend. If you put Requires=postgresql.service in your app.service, you’re saying, “My existence is meaningless without PostgreSQL.” Here’s what happens:

  1. When you start app.service, systemd will activate postgresql.service too.
  2. If postgresql.service fails to start or crashes at any point later, systemd will automatically stop your app.service. It’s a package deal. Failure is shared.

Wants is your much more chill, best-effort friend. Wants=postgresql.service means, “I would like PostgreSQL to be running, please. But if it’s not around or it blows up, that’s a you problem, not a me problem. I’ll still try to run.”

  1. When you start app.service, systemd will still activate postgresql.service.
  2. If postgresql.service fails to start, app.service carries on, blissfully unaware, and will probably fail in a more cryptic way later when it can’t connect to a database. If PostgreSQL crashes later, app.service is left running.

The choice is philosophical. Requires is for hard, non-negotiable dependencies (a web server needing its database). Wants is for nice-to-haves or things where you want the unit to attempt to start but don’t want its fate tied to the dependency.

Here’s what that looks like in a unit file for a fictional app:

# /etc/systemd/system/my-app.service
[Unit]
Description=My Brilliant Application
# A hard dependency
Requires=postgresql.service
# A soft dependency
Wants=redis.service
# We also want to make sure the network is up before we try anything
After=network.target postgresql.service redis.service

[Service]
ExecStart=/usr/bin/my-app
# ... rest of service config

The Ordering Dunce Cap: After and Before

Here’s the part that trips up everyone, so pay attention: Wants and Requires do not impose order. I’ll say it again. They create a dependency for activation, not for sequence.

This is the most common “why the hell isn’t this working?!” moment. You can Requires=postgresql.service all day long, and systemd will happily start your app and PostgreSQL at the exact same time. Your app will likely try to connect before the database is ready and fail spectacularly.

This is why we have After and Before. They are the ordering directives. They don’t cause the other unit to be started; they just say, “If both of us are starting, here’s who should go first.”

  • After=postgresql.service: “Start me AFTER postgresql.service is up.”
  • Before=postgresql.service: “Start me BEFORE postgresql.service is up.” (This is less common and usually used in the target unit’s config).

You almost always use After alongside Wants or Requires. It’s the one-two punch: “Bring that unit up and make sure it’s finished starting before you get to me.”

[Unit]
Description=My App
Requires=postgresql.service
# Ensure we start after postgres and the network are ready
After=network.target postgresql.service

[Service]
ExecStart=/usr/bin/my-app

The Pitfalls and The “Actually Correct” Way

So the “correct” setup for a hard dependency is Requires + After. But honestly? Most seasoned systemd users I know (including me) have largely abandoned Requires.

Why? Because it’s too brutal. If your database has a momentary hiccup and restarts, systemd will tear down your entire web application cluster alongside it. This creates a cascading failure instead of containing it. A service with Wants and After will stay running if the DB restarts; it might throw connection errors for a few seconds, but it will recover gracefully once the DB is back. This is almost always what you want.

The true best practice for essential dependencies is to use Wants and After for ordering, and then implement proper health checks and connection retries inside your application. Let your app be smart enough to handle a temporarily unavailable dependency. This is more resilient.

The Special Cases: BindsTo and PartOf

Sometimes, the brutality of Requires is exactly what you want. For an even tighter coupling, check out BindsTo. It acts like Requires but also adds a requirement that the unit must be actively running. If it stops for any reason (even successfully), your unit is stopped.

PartOf is similar but less extreme. It’s great for grouping services. If you have PartOf=multi-service-app.target in a unit’s config, then stopping or restarting that target will stop/restart your unit. But if your unit stops on its own, it won’t affect the target. It’s a one-way relationship.

Use BindsTo for things like a service that monitors a device file provided by another unit. If the device unit stops, your monitor must stop. Use PartOf to create logical groups of services you want to manage together.