How to check Linux service dependencies and runtime paths

Failed or repeat-starting systemd services often hide the real cause in a dependency edge, a drop-in override, or a runtime path that differs from the packaged unit. Checking the merged unit and parsed properties before changing the service separates missing prerequisites from executable, environment file, user, and directory problems.

systemctl cat shows the unit file and drop-ins in the order systemd applies them, while systemctl show prints the properties systemd actually loaded. That distinction matters because dependencies can come from Requires, Wants, After, install-time symlinks, socket activation, generated mount units, and implicit defaults.

Runtime path checks should focus on the executable from ExecStart, optional or required EnvironmentFile entries, WorkingDirectory, service identity, and directories such as RuntimeDirectory or StateDirectory. Run the checks with enough privileges to read protected unit files and paths, and avoid restarting a failing service until the missing dependency or path is understood.

Steps to check Linux service dependencies and runtime paths with systemctl:

  1. Display the merged unit file and drop-in overrides.
    $ systemctl --no-pager cat ssh.service
    # /usr/lib/systemd/system/ssh.service
    [Unit]
    Description=OpenBSD Secure Shell server
    Documentation=man:sshd(8) man:sshd_config(5)
    After=network.target nss-user-lookup.target auditd.service
    ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
    
    [Service]
    EnvironmentFile=-/etc/default/ssh
    ExecStartPre=/usr/sbin/sshd -t
    ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
    ExecReload=/usr/sbin/sshd -t
    ExecReload=/bin/kill -HUP $MAINPID
    KillMode=process
    Restart=on-failure
    RestartPreventExitStatus=255
    Type=notify
    RuntimeDirectory=sshd
    RuntimeDirectoryMode=0755
    
    [Install]
    WantedBy=multi-user.target
    Alias=sshd.service

    Replace ssh.service with the affected unit name. Include the suffix when checking a socket, timer, mount, or template instance.

  2. Print the parsed dependency properties for the unit.
    $ systemctl show ssh.service -p FragmentPath -p DropInPaths -p Requires -p Wants -p After -p Before
    Requires=system.slice sysinit.target ssh.socket
    Wants=sshd-keygen.service
    Before=shutdown.target
    After=basic.target network.target sshd-keygen.service systemd-journald.socket -.mount system.slice nss-user-lookup.target auditd.service sysinit.target ssh.socket
    FragmentPath=/usr/lib/systemd/system/ssh.service
    DropInPaths=

    FragmentPath identifies the main unit file. DropInPaths is blank when no override snippets are loaded for that unit.

  3. List the resolved units ordered before the service.
    $ systemctl list-dependencies --after --all --plain --full ssh.service
    ssh.service
      -.mount
      -.slice
      auditd.service
      ssh.socket
      system.slice
      sysinit.target
      dev-hugepages.mount
      systemd-journald.socket
      dev-mqueue.mount
      kmod-static-nodes.service
      ldconfig.service
    ##### snipped #####

    Use --before to inspect units ordered after the service. Use --reverse when the question is which other units depend on the selected unit.

  4. Print the parsed runtime path and identity properties.
    $ systemctl show ssh.service -p ExecStartPre -p ExecStart -p ExecReload -p EnvironmentFiles -p User -p Group -p WorkingDirectory -p RuntimeDirectory -p StateDirectory -p CacheDirectory -p LogsDirectory -p ConfigurationDirectory -p PIDFile
    PIDFile=
    ExecStartPre={ path=/usr/sbin/sshd ; argv[]=/usr/sbin/sshd -t ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }
    ExecStart={ path=/usr/sbin/sshd ; argv[]=/usr/sbin/sshd -D $SSHD_OPTS ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }
    ExecReload={ path=/usr/sbin/sshd ; argv[]=/usr/sbin/sshd -t ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }
    ExecReload={ path=/bin/kill ; argv[]=/bin/kill -HUP $MAINPID ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }
    EnvironmentFiles=/etc/default/ssh (ignore_errors=yes)
    WorkingDirectory=
    User=
    Group=
    RuntimeDirectory=sshd
    StateDirectory=
    CacheDirectory=
    LogsDirectory=
    ConfigurationDirectory=

    A leading dash in EnvironmentFile appears as ignore_errors=yes, so a missing file is allowed. The executable shown in path= is the program systemd will try to spawn.

  5. Check the executable and referenced files from the runtime properties.
    $ sudo ls -l /usr/sbin/sshd /etc/default/ssh /etc/ssh/sshd_config
    -rw-r--r-- 1 root root    133 Mar 12 14:14 /etc/default/ssh
    -rw-r--r-- 1 root root   4307 Apr 28 00:15 /etc/ssh/sshd_config
    -rwxr-xr-x 1 root root 396152 Apr 28 00:15 /usr/sbin/sshd

    A missing ExecStart binary or an unreadable required EnvironmentFile causes an immediate start failure and can contribute to start-limit-hit after repeated retries.

  6. Check runtime directories and other service paths.
    $ sudo ls -ld /run/sshd /var/log
    drwxr-xr-x 2 root root   40 Jun 13 20:31 /run/sshd
    drwxr-xr-x 1 root root 4096 Jun 13 20:31 /var/log

    Paths under /run/ may be created only while the service is running. A configured RuntimeDirectory tells systemd to create that directory for the service lifecycle.

  7. Confirm the final service state after the dependency and path review.
    $ systemctl show ssh.service -p LoadState -p ActiveState -p SubState -p Result -p NRestarts
    LoadState=loaded
    ActiveState=active
    SubState=running
    Result=success
    NRestarts=0

    LoadState=loaded confirms the unit definition was accepted. Result=success and NRestarts=0 help separate a stable service from a crash loop or a recently failed start.
    Related: How to check Linux service status