How to check service dependencies using systemctl

Checking a service's dependencies with systemctl shows which other units systemd pulls in, waits for, or orders ahead of the service before startup. That dependency view is often the fastest way to explain slow starts, unexpected activation chains, or a service that behaves differently after another unit changes.

systemd builds that graph from the unit file, any drop-in overrides, and implicit relationships it adds while loading the unit. Pull-in directives such as Requires=, Wants=, Requisite=, and BindsTo= answer which units are tied to the service, while After= and Before= answer when those units may start relative to each other.

The list-dependencies view only shows units currently loaded into memory by the service manager, so unloaded reverse relationships do not appear until something loads them. Socket-, timer-, and path-activated services may also depend more directly on their companion units than on the service unit alone, so checking the trigger unit can matter when the service itself is inactive.

Steps to check service dependencies using systemctl:

  1. Show the dependency and ordering properties systemd currently has loaded for the service.
    $ systemctl show -p Requires -p Wants -p Requisite -p BindsTo -p PartOf -p Conflicts -p After -p Before ssh.service
    Requires=system.slice sysinit.target ssh.socket
    Requisite=
    Wants=
    BindsTo=
    PartOf=
    Conflicts=shutdown.target
    Before=shutdown.target
    After=sysinit.target system.slice network.target -.mount systemd-journald.socket basic.target auditd.service ssh.socket

    Requires=, Wants=, Requisite=, and BindsTo= describe requirement relationships. After= and Before= are separate ordering rules and do not pull units in by themselves.

    Replace ssh.service with the exact unit name being investigated, including suffixes such as .service, .socket, .timer, or .target.

  2. Print the resolved dependency tree for the service.
    $ systemctl --no-pager list-dependencies --all --plain --full ssh.service | sed -n '1,10p'
    ssh.service
      ssh.socket
      system.slice
      -.slice
      sysinit.target
      systemd-tmpfiles-setup-dev-early.service
      local-fs-pre.target
      systemd-tmpfiles-setup-dev.service
      systemd-tmpfiles-setup.service
      local-fs.target

    –all expands dependencies beyond target units, and –plain removes tree glyphs so the output is easier to copy into notes, tickets, or change records.

    This tree is limited to units currently loaded into memory by systemd. A unit that has not been loaded yet will not appear in the reverse view even if its unit file declares a relationship.

  3. Narrow the view to units ordered before the service when startup sequencing is the main question.
    $ systemctl --no-pager list-dependencies --after --all --plain --full ssh.service | sed -n '1,12p'
    ssh.service
      -.mount
      -.slice
      auditd.service
      ssh.socket
      system.slice
      sysinit.target
      dev-mqueue.mount
      systemd-journald.socket
      modprobe@dm_mod.service
      system-modprobe.slice
      modprobe@drm.service

    With list-dependencies, –after shows units ordered before the specified unit, while –before shows units ordered after it.

  4. Read the effective unit file to see where the dependency directives are declared.
    $ systemctl --no-pager cat ssh.service | sed -n '1,16p'
    # /usr/lib/systemd/system/ssh.service
    [Unit]
    Description=OpenBSD Secure Shell server
    Documentation=man:sshd(8) man:sshd_config(5)
    After=network.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

    systemctl cat prints the vendor unit file followed by any drop-ins from /etc/systemd/system/<unit>.d/, which makes it easier to see whether a dependency came from the packaged unit or from a local override.

  5. Check the state of the dependency units that matter for the service.
    $ systemctl is-active ssh.socket system.slice sysinit.target systemd-journald.socket basic.target
    active
    active
    active
    active
    active

    Pick units from the Requires=, Wants=, or After= output that are directly relevant to the startup path being investigated.

    A unit listed only in After= is not necessarily a hard requirement. Treat an inactive dependency as a blocker only when it is also tied in by Requires=, Requisite=, BindsTo=, or by the workflow the service actually needs to complete.

  6. Inspect reverse relationships when the question is why a service keeps being activated by another unit.
    $ systemctl --no-pager list-dependencies --reverse --plain --full ssh.socket | sed -n '1,7p'
    ssh.socket
      ssh.service
      sockets.target
      basic.target
      initrd.target
      multi-user.target
      graphical.target

    Reverse checks are especially useful for socket-, timer-, and path-activated workloads because the trigger unit often explains why the service appears unexpectedly.