Table of Contents
- Understanding Systemd Services: A Primer
- What Are Systemd Service Dependencies?
- Types of Systemd Service Dependencies
- Defining Dependencies in Unit Files
- Tools for Inspecting and Managing Dependencies
- Troubleshooting Common Dependency Issues
- Best Practices for Designing Service Dependencies
- Advanced Topics: Targets and Dependency Chains
- Conclusion
- References
1. Understanding Systemd Services: A Primer
Before diving into dependencies, let’s recap the basics of systemd services. A systemd service is a unit of work managed by systemd, defined by a unit file (typically with a .service extension). Unit files are stored in standard locations:
/usr/lib/systemd/system/: Distro-provided services./etc/systemd/system/: User-defined or overridden services (takes precedence)./run/systemd/system/: Runtime-generated services.
A typical .service file has three main sections:
[Unit]: Metadata, descriptions, and dependencies (the focus of this blog).[Service]: Service-specific configuration (executable path, restart policy, user, etc.).[Install]: Configuration for enabling/disabling the service at boot (e.g.,WantedBy=multi-user.target).
Services can be in states like active (running), inactive (stopped), failed (crashed), or activating (starting up). Dependencies govern how these states interact between services.
2. What Are Systemd Service Dependencies?
Service dependencies are relationships between two or more systemd units (usually services) that dictate:
- Whether one service must start before another.
- Whether one service requires another to function.
- How services react to each other’s failures or restarts.
For example:
- A web server (e.g., Nginx) depends on the network being available (
network.target). - A database-backed application (e.g., a Python app) depends on the database (e.g., MySQL) being running.
Misconfigured dependencies can lead to failures (e.g., the app starts before the database, causing connection errors) or inefficiencies (e.g., unnecessary delays during boot).
3. Types of Systemd Service Dependencies
Systemd defines several dependency directives to model these relationships. Most live in the [Unit] section of a service file. Below are the key types:
Requires: Strict Runtime Dependencies
Directive: Requires=<service>
Behavior: Declares a strict dependency: the current service cannot function without the specified service. If the required service fails to start, the current service is not started. If the required service stops/crashes later, the current service is also stopped.
Example: A service myapp.service that requires mysql.service to run:
[Unit]
Description=My Database-Backed App
Requires=mysql.service
Caveat: Requires does not control order (use After/Before for that). If myapp starts before mysql (due to parallelization), it may fail even with Requires=mysql.service.
Wants: Weak Recommendations
Directive: Wants=<service>
Behavior: A weaker version of Requires. The current service recommends the specified service but can start without it. If the wanted service fails, the current service continues running.
Use Case: Optional dependencies (e.g., a logging service that enhances the app but isn’t critical).
Example:
[Unit]
Description=My App with Optional Logging
Wants=logging.service
Requisite: Pre-Flight Checks
Directive: Requisite=<service>
Behavior: Similar to Requires, but stricter: the specified service must already be active before the current service starts. If the requisite service is not running, the current service fails to start immediately (no attempt is made to start the requisite service).
Use Case: Services that depend on another service being pre-initialized (e.g., a plugin service requiring the main application to already be running).
Example:
[Unit]
Description=My App Plugin
Requisite=myapp-main.service
BindsTo: Tight Coupling
Directive: BindsTo=<service>
Behavior: Stronger than Requires. The current service is tightly bound to the specified service: if the bound service stops (even normally), the current service is stopped. If the bound service is restarted, the current service is also restarted (unless Restart=... is configured otherwise).
Use Case: Services that are part of a single logical unit (e.g., a master-worker process pair).
Example:
[Unit]
Description=Worker Process
BindsTo=master.service
After/Before: Ordering Control
Directive: After=<service> or Before=<service>
Behavior: Controls the start/stop order of services, but does not create a strict dependency. After=X means the current service starts after X starts. Before=Y means the current service starts before Y starts.
Key Point: After/Before only affect order, not whether the other service is running. Use with Requires/Wants to enforce both dependency and order.
Example: Ensure myapp starts after mysql and network.target:
[Unit]
Description=My App
Requires=mysql.service network.target
After=mysql.service network.target # Ensures order
Conflicts: Mutual Exclusion
Directive: Conflicts=<service>
Behavior: If the current service starts, the specified service is stopped (and vice versa).
Use Case: Services that cannot run simultaneously (e.g., two web servers on the same port: nginx.service and apache2.service).
Example:
[Unit]
Description=Nginx Web Server
Conflicts=apache2.service
OnFailure: Handling Service Failures
Directive: OnFailure=<service>
Behavior: Specifies a service to start if the current service fails (exits with non-zero status, crashes, etc.).
Use Case: Triggering alerts, fallbacks, or cleanup (e.g., sending an email on failure, or starting a backup service).
Example:
[Unit]
Description=My Critical App
OnFailure=send-alert.service
4. Defining Dependencies in Unit Files
Dependencies are defined in the [Unit] section of a .service file. Let’s combine multiple directives into a realistic example: a Node.js app (nodeapp.service) that depends on:
network.target(network connectivity).mysql.service(database).- Optional
redis.service(caching). - Must start after
mysqlandnetwork.target. - Sends an alert if it fails.
Sample Unit File: /etc/systemd/system/nodeapp.service
[Unit]
Description=Node.js Web Application
Documentation=https://example.com/docs
Requires=mysql.service network.target # Strict dependencies
Wants=redis.service # Optional dependency
After=mysql.service network.target redis.service # Order: start after these
OnFailure=alert-email.service # Action on failure
[Service]
User=appuser
WorkingDirectory=/opt/nodeapp
ExecStart=/usr/bin/node server.js
Restart=on-failure # Restart on non-zero exit code
[Install]
WantedBy=multi-user.target # Enable at boot under multi-user.target
Key Notes:
[Install]section’sWantedBy=multi-user.targetlinks the service tomulti-user.target(a boot target for non-graphical systems). When enabled (systemctl enable nodeapp), a symlink is created in/etc/systemd/system/multi-user.target.wants/.RequiresandWantsdefine runtime dependencies, whileWantedByin[Install]defines boot-time activation (which targets pull in the service).
5. Tools for Inspecting and Managing Dependencies
Systemd provides built-in tools to visualize and debug dependencies. Here are the most useful:
systemctl list-dependencies
Lists all dependencies of a service (both direct and indirect).
Examples:
- List dependencies of
nodeapp.service(whatnodeappdepends on):systemctl list-dependencies nodeapp.service - List reverse dependencies (what depends on
nodeapp):systemctl list-dependencies --reverse nodeapp.service
systemctl show
Shows low-level properties of a service, including dependencies.
Example: View all Requires, Wants, and After directives for nodeapp:
systemctl show nodeapp.service -p Requires -p Wants -p After
systemd-analyze
Visualizes boot and dependency chains.
systemd-analyze plot: Generates an SVG graph of all boot dependencies.systemd-analyze plot > dependencies.svgsystemd-analyze critical-chain: Shows the critical path during boot (the longest chain of dependencies that determines boot time).systemd-analyze critical-chain nodeapp.service
systemctl cat
Displays the full unit file (including overrides) to check for typos in dependencies.
Example:
systemctl cat nodeapp.service
6. Troubleshooting Common Dependency Issues
Even with careful configuration, dependency issues can arise. Here are common problems and fixes:
1. Circular Dependencies
Problem: Service A depends on B, and B depends on A. Systemd detects this and refuses to start either.
Fix: Use systemctl list-dependencies to identify the loop. Remove unnecessary Requires/Wants directives or split services to break the cycle.
2. Service Starts Before Its Dependency (Order Issues)
Problem: Requires=mysql.service is set, but the app starts before MySQL, causing connection errors.
Fix: Add After=mysql.service to enforce order.
3. Missing Dependencies
Problem: systemctl start nodeapp fails with “Dependency failed for Node.js Web Application”.
Debug: Check journalctl -u nodeapp.service for logs. Use systemctl list-dependencies nodeapp to verify required services exist and are enabled.
Fix: Install/enable the missing service (e.g., systemctl enable --now mysql.service).
4. Unintended Service Stops
Problem: nodeapp stops when redis (a Wants dependency) crashes.
Root Cause: Accidentally used Requires=redis.service instead of Wants=redis.service.
Fix: Replace Requires with Wants in the [Unit] section.
7. Best Practices for Designing Service Dependencies
To avoid issues, follow these guidelines:
- Minimize Dependencies: Only include critical dependencies. Fewer dependencies reduce complexity and boot time.
- Prefer
WantsOverRequires: UseRequiresonly for hard dependencies.Wantsmakes the system more resilient to optional service failures. - Always Pair
Requires/WantswithAfter/Before: Ensure services start in the correct order (e.g.,Requires=mysql.service+After=mysql.service). - Avoid Circular Dependencies: Design services to be modular. If two services depend on each other, merge them or introduce a shared dependency (e.g., a target).
- Test with
systemd-analyze verify: Validate unit files for syntax/dependency errors:systemd-analyze verify nodeapp.service - Document Dependencies: Add comments in unit files explaining why each dependency is needed (e.g.,
# Requires MySQL for database connections).
8. Advanced Topics: Targets and Dependency Chains
Systemd uses targets (.target units) to group services and model boot stages (e.g., network.target, multi-user.target). Targets have no [Service] section—they exist solely to order and group services.
Key Targets:
network.target: Represents network availability (but not all network interfaces; usenetwork-online.targetfor full connectivity).multi-user.target: Non-graphical multi-user mode (most servers boot here).graphical.target: Graphical mode (depends onmulti-user.target).
Dependency Chains in Boot:
During boot, systemd starts default.target (usually a symlink to multi-user.target or graphical.target). default.target depends on other targets/services, creating a chain:
basic.target → network.target → multi-user.target → nodeapp.service.
Visualizing with systemd-analyze plot helps identify bottlenecks (e.g., a slow network.target delaying all dependent services).
9. Conclusion
Systemd service dependencies are the backbone of reliable Linux service management. By mastering directives like Requires, Wants, After, and tools like systemctl list-dependencies, you can ensure services start in the right order, fail gracefully, and integrate seamlessly with the system.
Remember: the goal is to design minimal, explicit dependencies that balance robustness and flexibility. With careful planning and testing, you can avoid common pitfalls like circular dependencies and ensure your system boots efficiently and reliably.
10. References
- systemd.unit(5) Manual (official documentation for unit files).
- systemctl(1) Manual (commands for managing services).
- systemd-analyze(1) Manual (dependency analysis tools).
- DigitalOcean: Understanding Systemd Units and Unit Files (practical guide).