How to Diagnose Why a systemd Service Starts Unexpectedly: Dependency Chain Analysis


2 views

When encountering a systemd service that shows as disabled yet remains active (running), we need to investigate several angles:

# Verify actual status
systemctl is-enabled myservice
systemctl is-active myservice

# Check dependency tree
systemctl list-dependencies myservice --reverse

The key discovery here relates to how Requires= creates a two-way relationship:

[Unit]
Description=Problematic Service
Requires=otherservice.service
After=otherservice.service

[Service]
ExecStart=/usr/bin/sleep infinity

In this case, restarting otherservice will trigger myservice due to the Requires= directive, even if myservice is disabled.

These commands help identify why a service started:

# Show recent starts/restarts
journalctl -u myservice --since "1 hour ago"

# Show complete dependency tree
systemd-analyze dot myservice | dot -Tsvg > deps.svg

# Check activation triggers
systemctl show myservice -p Triggers

For cases where you only want to ensure another service runs when your service starts, but not vice versa:

[Unit]
Description=Safe Dependency Example
Wants=otherservice.service
After=otherservice.service

[Service]
ExecStart=/usr/bin/myapp

For services that should never auto-start:

# Add startup prevention
[Unit]
RefuseManualStart=yes
RefuseManualStop=no

# Or mask completely
sudo systemctl mask myservice

When working with systemd services, you might encounter a puzzling situation where a service marked as disabled is mysteriously active and running. This behavior often occurs due to implicit dependencies and systemd's unit activation logic.

To properly diagnose why a service is running, use these essential commands:

# Check service status and loaded configuration
systemctl status myservice.service

# List dependencies that might activate the service
systemctl list-dependencies --reverse myservice.service

# Show the unit's property values
systemctl show myservice.service | grep -E 'Requires|After|Wants'

# View the complete dependency tree
systemd-analyze dot myservice.service | dot -Tsvg > deps.svg

The most frequent reasons for unexpected service activation include:

  • Implicit Requires relationships in unit files
  • Socket activation or path activation triggers
  • Reverse dependencies (services that depend on yours)
  • Default target dependencies

Consider this service unit (/etc/systemd/system/myservice.service):

[Unit]
Description=My Service
Requires=otherservice.service
After=otherservice.service

[Service]
ExecStart=/usr/bin/myservice
Restart=on-failure

[Install]
WantedBy=multi-user.target

Even when myservice is disabled, restarting otherservice will start myservice due to the Requires directive. This is because Requires creates a strong dependency in both directions.

1. Replace Requires with Wants

Wants provides a weaker dependency that won't automatically start your service:

[Unit]
Description=My Service
Wants=otherservice.service
After=otherservice.service

2. Use BindsTo for Temporary Services

For services that should only run when their dependency is active:

[Unit]
Description=My Temporary Service
BindsTo=otherservice.service
After=otherservice.service

3. Explicitly Block Activation

Add these directives to prevent automatic starts:

[Unit]
RefuseManualStart=yes
StopWhenUnneeded=yes

For complex cases, examine the complete activation chain:

# View complete unit activation chain
journalctl _SYSTEMD_UNIT=myservice.service --no-pager --since "1 hour ago"

# Check for socket activation
systemctl list-sockets | grep myservice

# Verify if it's triggered by path activation
systemctl list-timers --all

Remember to reload systemd after making changes:

systemctl daemon-reload