Stop fighting systemd unit restarts when your process needs more than a polite signal
Technical Briefing | 10/19/2026
You probably rely on KillMode=control-group to tidy up your services, and that’s usually correct. But I have seen enough services hang during a reload because the main process spawned a sub-process that refused to die, eventually forcing me to hammer on a kill -9 after the systemd watchdog timed out. If you think the default signals are enough for your custom binaries, you might be in for a rude awakening when the service enters a permanent deactivating state.
Why the default sigterm is often ignored
Most developers write basic signal handlers that only catch SIGTERM. If your application starts sub-threads or interacts with legacy drivers, those threads might block the main loop, meaning your service never actually exits. Systemd waits for the TimeoutStopSec limit before it gets aggressive, and honestly, waiting ninety seconds for a service to stop is time you could spend doing literally anything else.
systemctl set-property my-service.service TimeoutStopSec=10s SendSIGKILL=yes
- Reduce TimeoutStopSec if your process doesn’t need minutes to flush buffers
- Set SendSIGKILL to yes if you prefer a hard stop over a hung process
- Override KillSignal to SIGQUIT if your app handles core dumps better than exits
Watching the exit behavior
If you are unsure how your service behaves, stop guessing and watch the transition. Open a separate terminal, run journalctl -f, and issue a restart. If you see the status flip to deactivating but it sits there for ages, you have a signal leak. Fix it in the unit file rather than cursing the service manager when the system finally decides to force it shut.
This is a low-effort fix that saves you from those three a.m. pages where the restart loop gets stuck because a port is still held open by a zombie child process. Next time a service fails to stop cleanly, stop assuming it’s a bug in the code and start telling systemd exactly how much patience you have.
