Accelerate Linux Boot Time: Analyzing and Optimizing Systemd Unit Dependencies

Use systemd-analyze, critical-chain, and unit dependency cleanup to find and fix slow Linux boot paths.

Accelerate Linux Boot Time: Analyzing and Optimizing Systemd Unit Dependencies

Linux boot time optimization starts with one question: what is actually blocking your machine from becoming usable? On most modern distributions, systemd starts services in parallel, but a slow unit or unnecessary ordering rule can still hold up the boot path.

By checking which units take time and which units sit on the critical chain, you can decide whether to tune, delay, or disable them. The examples below focus on systemd-analyze and small dependency changes that keep your system predictable.

Understanding the Systemd Boot Process

Systemd manages the startup process by executing services in parallel whenever possible. However, a service can only start when all its explicit and implicit dependencies are met. If Unit A requires Unit B to be fully active before it can proceed, Unit A is blocked by Unit B. Identifying these blocking dependencies is the first step toward acceleration.

Key Systemd Analysis Tools

Systemd provides several powerful command-line utilities to diagnose boot performance. The following tools are essential for pinpointing bottlenecks:

1. systemd-analyze (Overall View)

This command provides a high-level overview of the total time taken for the kernel, userspace initialization, and the time spent loading available targets.

systemd-analyze

Example Output Interpretation:

Component Time Taken
Kernel 1.234s
Initrd 0.500s
Userspace 5.789s
Total 7.523s

This quickly shows you if the bottleneck is in the kernel phase (firmware/driver loading) or the userspace phase (service startup).

2. systemd-analyze blame (Identifying Slow Units)

This command lists units sorted by how long they spent activating, with the longest times at the top.

systemd-analyze blame

Focus: Look at the top 10 entries. These are the services that are actively consuming time during startup. Note that a long initialization time might simply mean the service does a lot of work; the goal is to see if this work needs to happen during boot.

3. systemd-analyze critical-chain (Dependency Analysis)

This command shows the dependency chain that leads to the boot target (usually graphical.target or multi-user.target). It highlights the sequence of units that must complete before the system is considered fully booted.

systemd-analyze critical-chain

Units listed in the critical chain are primary targets for optimization because delaying them delays the entire system boot.

4. systemd-analyze plot (Visualizing Boot Sequence)

For a graphical representation of parallelism and blocking, use the plot command, which generates an SVG file:

systemd-analyze plot > boot_analysis.svg
# Open boot_analysis.svg in a web browser

This graph visually demonstrates which services are running in parallel and which are waiting for others, making dependency issues immediately apparent.

Optimization Techniques: Modifying Unit Files

Once you have identified slow or blocking units using the tools above, optimization involves either speeding up the unit itself or changing when it needs to run.

1. Addressing Slow Units Identified by blame

If a service listed high on the blame output (e.g., slow-database.service takes 10 seconds) is not immediately required for basic system operation (like logging in or basic networking), consider delaying it.

Action: Change its startup dependency level.

  • If it currently starts at multi-user.target, check whether it can start from a timer, socket, path unit, or manual command instead.
  • If the service is optional, disabling it is usually safer than changing core dependency behavior. Use DefaultDependencies=no only when you understand the default ordering systemd would normally add for that unit type.

2. Optimizing Dependencies using Wants, Requires, and After

Unit files control execution order using dependency directives. Misconfiguration here is a common source of unnecessary sequential execution.

Dependency Types:

  • Requires=: A strong dependency. If the required unit fails, this unit will fail too.
  • Wants=: A weak dependency. This unit starts if the wanted unit is available, but will still attempt to start if the wanted unit fails.
  • After=: Ordering directive. This unit will only start after the specified unit has finished starting (regardless of success).
  • Before=: Ordering directive. This unit must start before the specified unit.

Best Practice Tip: Prefer Wants over Requires for optional relationships. Wants= changes failure behavior, not ordering by itself. A wanted unit can still start in parallel unless you also add an ordering rule such as After=.

Removing Unnecessary After= Constraints

The most effective way to speed up boot time is to eliminate unnecessary ordering constraints. If Unit A does not functionally rely on Unit B being started before Unit A begins, remove the After=unit-b.service line from Unit A's definition.

Example Modification (Conceptual):

Suppose your custom application unit app.service unnecessarily waits for the network configuration service:

# /etc/systemd/system/app.service
[Unit]
Description=My Application
Requires=network.target
After=network.target  <-- Potentially unnecessary wait!

[Service]
ExecStart=/usr/bin/myapp

If your application only needs a local loopback interface or only needs to establish a local file lock, waiting for the full network stack (network.target) might be wasting several seconds. If you confirm the application does not truly need the external network, remove the After=network.target line. Systemd will then attempt to start app.service as soon as possible in parallel with network setup.

3. Masking Unneeded Services

If systemd-analyze blame shows a service running that you absolutely do not need (e.g., unnecessary Bluetooth support on a server, or a specific hardware monitor), disabling or masking it stops it from starting entirely.

  • Disable: systemctl disable <unit> (Stops it from starting on future boots).
  • Mask (Stronger): systemctl mask <unit> (Links the unit to /dev/null, preventing manual start attempts as well).
# Example: Masking the ModemManager if no cellular modem is present
sudo systemctl mask ModemManager.service

Reloading and Verifying Changes

After modifying any unit file (especially those placed in /etc/systemd/system/), you must tell systemd to reload its configuration daemon before rebooting to test:

sudo systemctl daemon-reload

# Then, check dependencies or status before rebooting
systemctl list-dependencies myapp.service

Finally, always reboot the system to measure the true impact on the boot sequence.

sudo reboot

After rebooting, immediately run systemd-analyze again to quantify the time savings achieved through your optimizations.

Takeaway

Treat boot tuning as a small change loop: measure with systemd-analyze, find the units on the critical path, remove only the ordering rules you can justify, then reboot and measure again. The safest wins usually come from disabling unneeded services, converting work to timers or socket activation, and removing unnecessary After= lines from your own units.