Soft2Soft Ops Practical knowledge base
systemd

How to Fix a systemd Service Stuck in the Deactivating State

3 views
Linux systemd диагностика

A service remains in the deactivating state when systemd has already started stopping the unit but is still waiting for the ExecStop= command, the main process, or other processes in the control group to exit. To resolve the problem, determine exactly what systemd is waiting for, terminate that process safely, and then configure a stop timeout to prevent the service from hanging again.

Prerequisites and limitations

The commands below apply to systems that use systemd and require root privileges or access through sudo. Replace my.service with the actual unit name. Do not send SIGKILL until you have confirmed that the process is not performing a critical write to a database, file system, or other storage.

The actual stop behavior depends on the unit configuration, including Type=, KillMode=, TimeoutStopSec=, SendSIGKILL=, and any ExecStop= commands. Inspect the effective settings of the installed unit rather than only the file under /etc/systemd/system.

Step 1. Confirm the state and identify the current operation

First, retrieve the service status without paginated output:

sudo systemctl status my.service --no-pager -l

Then query the properties that show the stop phase, processes, and effective timeout:

sudo systemctl show my.service \
  -p ActiveState \
  -p SubState \
  -p Result \
  -p MainPID \
  -p ControlPID \
  -p ExecMainCode \
  -p ExecMainStatus \
  -p TimeoutStopUSec \
  -p KillMode \
  -p SendSIGKILL

Interpret the main fields as follows:

  • ActiveState=deactivating confirms that the unit is being stopped.
  • ControlPID usually identifies the process running the current control command, such as ExecStop=.
  • MainPID contains the PID of the main process if systemd is still tracking it.
  • TimeoutStopUSec shows the effective stop timeout.
  • KillMode determines which processes systemd terminates when stopping the unit.

Also inspect the systemd job queue:

sudo systemctl list-jobs

If a stop job is listed for my.service, systemd still considers the stop operation to be in progress. Other pending jobs may explain why related units are also blocked.

Step 2. Inspect the stop logs

Review messages from the service and systemd for the current boot:

sudo journalctl -u my.service -b --no-pager -n 200

To monitor the journal in real time, use:

sudo journalctl -u my.service -f

Look for the following signs:

  • a stop command starting without a corresponding completion message;
  • messages indicating that the stop timeout was exceeded;
  • repeated attempts to close connections or child processes;
  • errors accessing sockets, PID files, file systems, or network resources;
  • messages such as “process remains running after unit stopped” or similar warnings about processes left in the cgroup.

If the service journal does not contain enough information, filter systemd messages by the unit name:

sudo journalctl -b --no-pager | grep -F 'my.service'

Step 3. Identify the stuck process

Retrieve the PIDs of the main process and the control process:

sudo systemctl show my.service -p MainPID -p ControlPID

For each nonzero PID, inspect the process state:

ps -o pid,ppid,stat,etime,wchan:32,cmd -p PID

Replace PID with the actual number. The STAT column helps identify the nature of the hang:

  • S — interruptible sleep; the process normally responds to signals.
  • R — running on a CPU.
  • D — uninterruptible kernel sleep, often related to a disk, network file system, driver, or another I/O operation.
  • Z — an exited zombie process that must be reaped by its parent process.

To display all processes associated with the service, use:

sudo systemd-cgls --unit my.service

The available systemd-cgls options may differ between systemd versions. Check the supported syntax locally:

systemd-cgls --help

If a process is in the D state, SIGKILL does not guarantee immediate termination. The process can exit only after it returns from the uninterruptible kernel wait. In this case, investigate stalled I/O, unavailable storage, a network file system, or a driver problem. A forced reboot should be the last resort after assessing the risk of data corruption.

Step 4. Stop the service, from the least disruptive method to the most forceful

Retry a normal stop

First, retry the normal stop operation while monitoring the journal:

sudo systemctl stop my.service

If the command waits indefinitely in an interactive session, open a second session and continue the diagnosis there. Do not run multiple parallel stop and restart commands. They add jobs to the queue but do not resolve the underlying cause of the hang.

Send SIGTERM to all service processes

If the processes do not exit normally, send them a termination signal through systemd:

sudo systemctl kill --kill-whom=all --signal=SIGTERM my.service

Then check the state again:

sudo systemctl status my.service --no-pager -l
sudo systemctl show my.service -p ActiveState -p SubState -p MainPID -p ControlPID

Force termination

If the process does not respond to SIGTERM, the data has been saved, or an abrupt shutdown is acceptable, send SIGKILL to all processes in the unit:

sudo systemctl kill --kill-whom=all --signal=SIGKILL my.service

Check whether the processes have disappeared:

sudo systemd-cgls --unit my.service
sudo systemctl status my.service --no-pager -l

If the PID remains and is in the D state, sending more signals will not resolve the problem. Inspect the kernel messages:

sudo journalctl -k -b --no-pager -n 200

Pay particular attention to messages about block device errors, I/O timeouts, hung tasks, NFS, FUSE, iSCSI, and file-system failures. The appropriate fix depends on the source of the wait: restoring the storage system, bringing the network resource back online, unmounting the problematic file system, or rebooting the host in a controlled manner.

Step 5. Return the unit to an operational state

After the stuck processes have exited, check the final state:

sudo systemctl is-active my.service
sudo systemctl is-failed my.service
sudo systemctl status my.service --no-pager -l

If the service has entered the failed state, clear the recorded failure state:

sudo systemctl reset-failed my.service

After resolving the underlying cause, start the service:

sudo systemctl start my.service

Verify the result:

sudo systemctl is-active my.service
sudo systemctl status my.service --no-pager -l
sudo journalctl -u my.service -b --no-pager -n 100

The expected verifiable result is that systemctl is-active prints active and the journal does not show a new cycle of a stuck stop operation. For a one-shot service, inactive may be the normal final state, depending on Type= and RemainAfterExit=.

Step 6. Fix the unit file to prevent the problem from recurring

Display the complete effective unit file, including override fragments:

sudo systemctl cat my.service

Do not edit the unit file under /usr/lib/systemd/system or /lib/systemd/system directly. A package upgrade may overwrite it. Create a drop-in instead:

sudo systemctl edit my.service

Example of a bounded stop configuration:

[Service]
TimeoutStopSec=30s
KillMode=control-group
SendSIGKILL=yes

TimeoutStopSec=30s is an example, not a universal value. A database, message queue, or application that performs lengthy state persistence may need more time. The value should exceed the normal graceful-shutdown duration measured from logs and metrics.

KillMode=control-group instructs systemd to terminate processes in the service control group rather than only the main process. This usually prevents a child process from remaining alive after its parent exits. Before changing this setting, confirm that the service does not intentionally start processes that must survive when the unit stops.

SendSIGKILL=yes allows systemd to apply final forced termination after the relevant timeout expires. Do not use SendSIGKILL=no without a clear operational reason, because an unresponsive process may leave the unit stuck while stopping.

After saving the override, verify the unit-file syntax:

sudo systemd-analyze verify my.service

Then reload the manager configuration:

sudo systemctl daemon-reload

Confirm that the expected settings are effective:

sudo systemctl show my.service \
  -p TimeoutStopUSec \
  -p KillMode \
  -p SendSIGKILL

Checking ExecStop

If ControlPID was nonzero while the unit was stuck, the likely cause is an ExecStop= command. Retrieve its effective value:

sudo systemctl show my.service -p ExecStop

Review the stop script against the following criteria:

  • it does not wait for interactive input;
  • network requests and calls to external APIs have their own timeouts;
  • any loop that waits for a PID has a time limit;
  • the PID file is checked for freshness, and a PID is not used without verifying the corresponding process;
  • a stop failure returns a nonzero exit code instead of being hidden by an infinite retry loop;
  • the script does not start a background process and exit before the service has actually stopped.

Do not add ExecStop=/bin/kill -9 ... as the first solution. Systemd can already send signals to unit processes and track their cgroup. The ExecStop= command should perform application-specific graceful shutdown, while forced termination should be left to systemd’s timeout mechanism and kill policy.

Final checklist

  1. Check ActiveState, SubState, MainPID, and ControlPID.
  2. Review the service journal for the current boot.
  3. Determine the state of the stuck process with ps.
  4. Inspect all processes in the service cgroup.
  5. Send SIGTERM first, then use SIGKILL only when the risk is acceptable.
  6. If the process is in the D state, diagnose I/O and inspect kernel messages.
  7. After the processes exit, run reset-failed and start the service.
  8. Configure a justified TimeoutStopSec value and verify KillMode.
  9. Validate the unit with systemd-analyze verify and confirm the effective settings with systemctl show.

Sources

The following are official systemd project pages. To match the installed version precisely, also use the local man systemctl, man systemd.service, and man systemd.kill commands. The current contents of the online pages cannot be verified in this environment, so version-specific differences should be checked against the local package documentation.