The common misconception that shutdown -h now
behaves like kill -9
isn't entirely accurate. The shutdown process in Linux follows this sequence:
1. System sends SIGTERM (signal 15) to all processes
2. Waits for processes to terminate gracefully
3. After timeout, sends SIGKILL (signal 9) to remaining processes
4. Unmounts filesystems
5. Powers off the system
The issue isn't with the shutdown command itself, but with:
- Default timeout being too short (often just a few seconds)
- Poorly written applications ignoring SIGTERM
- Critical services not implementing proper signal handlers
To check how your system handles shutdown, examine the systemd configuration:
# Check default timeout
systemctl show -p DefaultTimeoutStopUSec
# Check service-specific timeouts
systemctl show servicename | grep Timeout
Option 1: Extended Timeout
# For immediate shutdown with longer timeout (30 seconds)
shutdown -h +0 --no-wall
# Alternative systemd method
systemd-run --on-active=30s --unit=delayed-shutdown.service systemctl poweroff
Option 2: Service-Specific Solutions
For critical services, implement proper systemd service files:
[Service]
ExecStop=/usr/local/bin/myapp_graceful_shutdown.sh
TimeoutStopSec=30
KillSignal=SIGTERM
FinalKillSignal=SIGKILL
For databases or other complex services:
#!/bin/bash
# Graceful shutdown sequence for MySQL
mysql_shutdown() {
timeout 30 mysqladmin -uroot -p$MYSQL_ROOT_PASSWORD shutdown
if [ $? -ne 0 ]; then
echo "Forcing MySQL shutdown"
kill -9 $(pidof mysqld)
fi
}
# Main shutdown handler
case $1 in
start)
# Normal startup
;;
stop)
mysql_shutdown
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
;;
esac
To audit your shutdown process:
# Enable verbose logging
systemd-analyze set-log-level debug
# After next shutdown/reboot, check logs
journalctl -b -1 | grep -i 'shutdown\|sigterm\|sigkill'
In containerized environments, additional precautions are needed:
# Kubernetes pod example
spec:
containers:
- name: myapp
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 30 && /opt/app/graceful_shutdown.sh"]
terminationGracePeriodSeconds: 60
Contrary to some misconceptions, shutdown -h now
does not perform the equivalent of kill -9
system-wide. The shutdown command actually initiates a proper shutdown sequence that:
- Sends SIGTERM (signal 15) to all processes first
- Waits briefly for processes to terminate gracefully
- Only forces termination with SIGKILL (signal 9) if processes don't respond
Here's what happens during a standard shutdown:
1. System switches to runlevel 0 (halt) or 6 (reboot)
2. init sends SIGTERM to all processes
3. Default 5-second wait period begins
4. After timeout, SIGKILL is sent to remaining processes
5. Filesystems are unmounted
6. System powers off
For critical services needing more cleanup time, consider these approaches:
1. Increasing the default timeout
# Give processes 30 seconds to shutdown
shutdown -h +0 --timeout=30
2. Creating custom systemd service files
[Unit]
Description=My Critical Service
Before=shutdown.target reboot.target halt.target
[Service]
Type=simple
ExecStart=/usr/bin/my-service
TimeoutStopSec=30
KillSignal=SIGTERM
FinalKillSignal=SIGKILL
[Install]
WantedBy=multi-user.target
3. Handling signals in your applications
Example Python signal handler:
import signal
import sys
def cleanup(signum, frame):
# Perform cleanup operations
sys.exit(0)
signal.signal(signal.SIGTERM, cleanup)
signal.signal(signal.SIGINT, cleanup)
Check your system logs after shutdown:
journalctl -b -1 | grep -i "stopping\|sigterm\|sigkill"
Or test with a dry run:
systemd-analyze verify /lib/systemd/system/*.service
CentOS/RHEL: Systemd-based since version 7
Ubuntu: Systemd since 15.04
Debian: Systemd since Jessie (8.x)
For legacy SysV init systems, modify the shutdown sequence in /etc/init.d/halt
and /etc/init.d/reboot
.