Table of Contents
-
- What Are Targets?
- Targets vs. Runlevels
- Common Built-in Targets
-
- Dependencies:
Wants,Requires,Before, andAfter - Target Activation and Boot Flow
- Dependencies:
-
Creating Custom Service Chains with Targets
- Step 1: Define a Custom Target
- Step 2: Create Service Units with Dependencies
- Step 3: Link Services to the Target
- Step 4: Enable and Test the Target
-
Practical Example: Web Application Stack
- Scenario Overview
- Target and Service Files
- Verification and Testing
Understanding Systemd Targets
What Are Targets?
In systemd, a target is a special type of unit (.target file) that groups other systemd units (services, sockets, mounts, etc.) to define a specific system state. Unlike service units (.service), targets don’t execute commands directly. Instead, they act as “markers” or “coordinators” that trigger the activation of other units in a predefined order.
Think of targets as playbooks: they don’t do the work themselves, but they ensure the right tools (services) are activated at the right time.
Targets vs. SysV Runlevels
If you’re familiar with traditional SysV init systems, targets replace the concept of runlevels. Runlevels were numbered (0 to 6) and represented fixed system states (e.g., runlevel 3 = multi-user text mode, runlevel 5 = graphical mode).
Systemd targets are more flexible:
- They have human-readable names (e.g.,
multi-user.target,graphical.target). - They support nested dependencies (targets can depend on other targets).
- They are not limited to a fixed set of states (you can create custom targets).
For reference, here’s how common runlevels map to systemd targets:
| SysV Runlevel | Systemd Target | Description |
|---|---|---|
| 0 | poweroff.target | Shutdown and power off the system. |
| 1 | rescue.target | Single-user mode for recovery. |
| 3 | multi-user.target | Multi-user text mode (no GUI). |
| 5 | graphical.target | Multi-user mode with graphical interface. |
| 6 | reboot.target | Reboot the system. |
Common Built-in Targets
Systemd provides many pre-defined targets. Here are some key ones:
basic.target: Minimal target with essential services (e.g., mount points, sockets).multi-user.target: Standard multi-user, non-graphical system state (most servers use this).graphical.target: Builds onmulti-user.targetand starts the GUI (e.g., GNOME, KDE).network.target: Ensures network connectivity is available.sound.target: Activates sound card drivers and services.timers.target: Triggers timer units (systemd’s cron replacement).
How Systemd Targets Work
Targets rely on dependencies to determine which units to activate and in what order. Systemd uses directives in unit files (both targets and services) to resolve these dependencies.
Key Dependency Directives
To define relationships between units, use these directives in the [Unit] section of .target or .service files:
| Directive | Purpose |
|---|---|
Requires | Strong dependency: If the required unit fails, this unit is also stopped. |
Wants | Weak dependency: Encourages the required unit to start, but doesn’t fail if it doesn’t. |
Before | This unit starts before the specified unit. |
After | This unit starts after the specified unit. |
Target Activation Flow
When systemd boots, it starts with default.target (a symlink to the default target, usually multi-user.target or graphical.target). It then resolves all dependencies of default.target (targets and services) and activates them in order.
For example, graphical.target depends on multi-user.target, which depends on basic.target, and so on. Each target pulls in the units it needs, creating a chain of activation.
Creating Custom Service Chains with Targets
While built-in targets handle most common scenarios, custom targets let you define specialized service chains (e.g., a target for a web app stack, IoT device, or backup service). Here’s how to create one:
Step 1: Define a Custom Target
Create a .target file in /etc/systemd/system/ (system-wide) or ~/.config/systemd/user/ (user-specific).
Example: webapp.target
# /etc/systemd/system/webapp.target
[Unit]
Description=Custom Target for Web Application Stack
Documentation=man:webapp(1)
# Ensure the system is in multi-user mode before starting our target
Requires=multi-user.target
After=multi-user.target
[Install]
# Make this target start when multi-user.target is activated
WantedBy=multi-user.target
[Unit]: Metadata and dependencies.Requires=multi-user.targetensures the system is fully booted before our target starts.[Install]: Defines how the target is enabled.WantedBy=multi-user.targetcreates a symlink inmulti-user.target.wants/, somulti-user.targettriggers our target.
Step 2: Create Service Units with Dependencies
Next, create .service files for the services in your chain. Define their dependencies using Before/After to control order.
Example 1: PostgreSQL Database (postgresql-webapp.service)
# /etc/systemd/system/postgresql-webapp.service
[Unit]
Description=PostgreSQL Database for Web App
Documentation=https://www.postgresql.org/docs/
# Start after network is up
After=network.target
# Weakly depend on the webapp target (optional)
Wants=webapp.target
[Service]
Type=notify
User=postgres
ExecStart=/usr/bin/postgres -D /var/lib/postgresql/14/main
Restart=on-failure
[Install]
# Enable this service when webapp.target is enabled
WantedBy=webapp.target
Example 2: Redis Cache (redis-webapp.service)
# /etc/systemd/system/redis-webapp.service
[Unit]
Description=Redis Cache for Web App
After=postgresql-webapp.service # Start after PostgreSQL
Wants=webapp.target
[Service]
Type=simple
ExecStart=/usr/bin/redis-server /etc/redis/webapp.conf
Restart=always
[Install]
WantedBy=webapp.target
Example 3: Node.js App (nodeapp.service)
# /etc/systemd/system/nodeapp.service
[Unit]
Description=Node.js Web Application
# Start after both PostgreSQL and Redis
After=postgresql-webapp.service redis-webapp.service
# Strong dependency: if either database or cache fails, stop the app
Requires=postgresql-webapp.service redis-webapp.service
Wants=webapp.target
[Service]
Type=node
WorkingDirectory=/opt/webapp
ExecStart=/usr/bin/node server.js
Restart=on-failure
[Install]
WantedBy=webapp.target
Step 3: Link Services to the Target
The [Install] section of each service file uses WantedBy=webapp.target, which links the service to our custom target. When the target is enabled, systemd creates symlinks in /etc/systemd/system/webapp.target.wants/ for each service.
Step 4: Enable and Test the Target
Reload systemd to detect new units, then enable and start the target:
# Reload systemd manager configuration
sudo systemctl daemon-reload
# Enable the target (persists across reboots)
sudo systemctl enable webapp.target
# Start the target immediately
sudo systemctl start webapp.target
Practical Example: Web Application Stack
Scenario Overview
Let’s build a service chain for a web app with three components:
- PostgreSQL: Database (must start first).
- Redis: Cache (starts after PostgreSQL).
- Node.js App: Web server (starts after both database and cache).
Target and Service Files
We’ll use the webapp.target and service files defined earlier. Here’s how they work together:
webapp.targetis triggered bymulti-user.targetat boot.webapp.targetactivatespostgresql-webapp.service,redis-webapp.service, andnodeapp.serviceviaWantedBy=webapp.target.- Dependencies (
After,Requires) ensure order: PostgreSQL → Redis → Node.js App.
Verification and Testing
Check Target Status
sudo systemctl status webapp.target
# Output should show "active (active)" if the target is running
List Target Dependencies
systemctl list-dependencies webapp.target --no-pager
# Should list postgresql-webapp.service, redis-webapp.service, nodeapp.service
Check Service Order
Use journalctl to verify startup order:
journalctl -u postgresql-webapp -u redis-webapp -u nodeapp --since "10 minutes ago"
# Look for timestamps to confirm PostgreSQL starts first, then Redis, then Node.js
Simulate a Failure
Test strong dependencies: Stop PostgreSQL and check if the Node.js app stops (due to Requires=postgresql-webapp.service):
sudo systemctl stop postgresql-webapp.service
sudo systemctl status nodeapp.service
# Output should show "inactive (failed)" for nodeapp.service
Best Practices for Efficient Service Chains
-
Prefer
WantsOverRequires: UseWantsfor most dependencies to avoid breaking the entire chain if one service fails (e.g., a non-critical cache). ReserveRequiresfor essential dependencies (e.g., a database). -
Order with
Before/After: Explicitly define order withBefore/Afterto avoid race conditions. For example,After=network.targetensures network is ready before a service starts. -
Avoid Circular Dependencies: Systemd cannot resolve circular dependencies (e.g., Service A requires Service B, which requires Service A). Use
systemctl list-dependenciesto check for loops. -
Test with
systemd-analyze: Usesystemd-analyze plot > boot.svgto visualize the boot process and identify bottlenecks in your service chain. -
Document the Target: Add
Documentationdirectives to.targetand.servicefiles to explain their purpose (e.g.,Documentation=https://example.com/webapp-docs). -
Use Template Units for Multiple Instances: For services with multiple instances (e.g.,
[email protected],[email protected]), use template units to reduce redundancy.
Troubleshooting Target and Service Dependencies
Common Issues and Fixes
Service Not Starting
- Check dependencies: Use
systemctl list-dependencies <service>.serviceto ensure all required units are active. - Inspect logs:
journalctl -u <service>.service --no-pagerfor error messages (e.g., “Failed to start due to unmet dependency”).
Target Not Activating
- Ensure the target is enabled:
sudo systemctl is-enabled webapp.target(should return “enabled”). - Check
[Install]section: VerifyWantedBy=points to a target that’s part of the boot process (e.g.,multi-user.target).
Incorrect Startup Order
- Use
Afterinstead ofBeforeif services start too early. - Check for missing
Afterdirectives (e.g., a service might needAfter=network-online.targetinstead ofnetwork.targetfor full connectivity).
Slow Boot Time
- Use
systemd-analyze blameto identify slow services in your chain. - Optimize service startup (e.g., reduce
ExecStartdelays, useType=simpleinstead ofType=oneshotfor fast services).
Conclusion
Systemd targets are a powerful tool for creating efficient, reliable service chains. By grouping services with dependencies and custom targets, you can ensure your system starts (and stops) in a controlled, predictable order. Whether you’re managing a server, IoT device, or home lab, mastering targets will simplify service management and reduce downtime.
Start small: Define a custom target for a simple stack (e.g., a blog with Nginx and MySQL), test dependencies, and gradually expand to more complex scenarios. With practice, you’ll build service chains that are robust, maintainable, and tailored to your needs.