Reliability and resources
Set timeouts and stop cleanly
Give startup and shutdown bounded time while letting the application handle its normal termination signal.
When you stop a service, systemd sends a signal and waits. By default that is SIGTERM, then a 90 second wait, then SIGKILL for anything still alive.
SIGTERM is the polite request. Your application should use that window to stop accepting work, finish in-flight requests, and flush what it must.
SIGKILL cannot be caught. Anything the process had not written yet is lost. The goal of this lesson is to make sure your service never gets that far in normal operation.
Set bounds from measured behavior
[Service]
TimeoutStartSec=30
TimeoutStopSec=20
Set TimeoutStartSec= and TimeoutStopSec= from measured behavior, not from guesses. If your application drains connections in five seconds, twenty is a comfortable stop bound. If startup ever legitimately takes a minute, a 30 second start timeout kills healthy starts, and you have created a new failure.
KillSignal= changes the initial signal. The default SIGTERM is normally correct. It is the signal every runtime and framework documents for graceful shutdown. Change it only for software that documents something else.
Avoid KillMode=process. It signals only the main process and can leave children behind, still holding ports and files after systemd considers the service stopped. The default is control-group, which signals the whole process tree. That is almost always what you mean.
Handle the signal in the app
The unit can give the app time. The app still has to use it. In Node.js the handler is short:
process.on('SIGTERM', () => {
server.close(() => process.exit(0))
})
server.close() stops accepting new connections and waits for the open ones to finish. Then the process exits cleanly. Without this handler, Node.js dies immediately on SIGTERM and drops every request in flight.
Watch a stop happen
Follow the journal in one terminal while you stop the service in another:
journalctl -u demo-api.service -f
sudo systemctl stop demo-api.service
A clean stop logs the application’s own shutdown messages, then Deactivated successfully. A service that ignores SIGTERM looks like this instead:
Stopping demo-api.service - Demo API server...
demo-api.service: State 'stop-sigterm' timed out. Killing.
demo-api.service: Killing process 1423 (node) with signal SIGKILL.
demo-api.service: Main process exited, code=killed, status=9/KILL
Read the status=9/KILL line as a bug report. If you see stop-sigterm timed out, the fix is in the application first: handle SIGTERM and exit. Raising TimeoutStopSec= only hides the missing handler and makes every deploy wait longer.
Try this with a test service while following its journal. Confirm which signal it receives, how long it needs, and whether every child process exits. Once you have those three numbers, set the timeouts from them.
Lesson completed