BASH
Graceful Service Restart with PID File and Health Check
Implement a robust Bash script to gracefully stop, start, and restart a service using a PID file, including a basic HTTP health check for reliability in deployments.
#!/bin/bash
SERVICE_NAME="my_web_service"
PID_FILE="/var/run/$SERVICE_NAME.pid"
LOG_FILE="/var/log/$SERVICE_NAME.log"
START_CMD="node /path/to/your/app.js > $LOG_FILE 2>&1 & echo \$! > $PID_FILE" # Example: Node.js app
HEALTH_CHECK_URL="http://localhost:3000/health" # Example: Health check endpoint
HEALTH_CHECK_RETRIES=10
HEALTH_CHECK_DELAY=2 # seconds
start_service() {
if [ -f "$PID_FILE" ] && kill -0 "$(cat "$PID_FILE")" >/dev/null 2>&1; then
echo "$SERVICE_NAME is already running (PID: $(cat "$PID_FILE"))."
return 0
fi
echo "Starting $SERVICE_NAME..."
eval "$START_CMD" # Use eval to execute the command with proper PID capture
# Give service a moment to start and write PID
sleep 2
if [ -f "$PID_FILE" ] && kill -0 "$(cat "$PID_FILE")" >/dev/null 2>&1; then
echo "$SERVICE_NAME started with PID: $(cat "$PID_FILE")."
check_health
else
echo "Failed to start $SERVICE_NAME. Check $LOG_FILE for details."
return 1
fi
}
stop_service() {
if [ ! -f "$PID_FILE" ]; then
echo "$SERVICE_NAME is not running (PID file not found)."
return 0
fi
PID=$(cat "$PID_FILE")
if kill -0 "$PID" >/dev/null 2>&1; then
echo "Stopping $SERVICE_NAME (PID: $PID)..."
kill "$PID" # Send SIGTERM for graceful shutdown
# Wait for the process to terminate
for i in $(seq 1 10); do
if ! kill -0 "$PID" >/dev/null 2>&1; then
echo "$SERVICE_NAME stopped."
rm -f "$PID_FILE"
return 0
fi
sleep 1
done
echo "Warning: $SERVICE_NAME (PID: $PID) did not terminate gracefully. Forcing kill."
kill -9 "$PID" # Force kill if not stopped gracefully
rm -f "$PID_FILE"
return 1
else
echo "$SERVICE_NAME is not running (stale PID file found). Removing $PID_FILE."
rm -f "$PID_FILE"
return 0
fi
}
restart_service() {
stop_service
sleep 2 # Give time for port release/cleanup
start_service
}
check_health() {
if [ -z "$HEALTH_CHECK_URL" ]; then
echo "No health check URL configured for $SERVICE_NAME."
return 0
fi
echo "Performing health check for $SERVICE_NAME at $HEALTH_CHECK_URL..."
for i in $(seq 1 $HEALTH_CHECK_RETRIES); do
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_CHECK_URL")
if [ "$HTTP_STATUS" -eq 200 ]; then
echo "$SERVICE_NAME health check successful (HTTP 200)."
return 0
else
echo "Health check attempt $i/$HEALTH_CHECK_RETRIES: HTTP Status $HTTP_STATUS. Retrying in $HEALTH_CHECK_DELAY seconds..."
sleep "$HEALTH_CHECK_DELAY"
fi
done
echo "Error: $SERVICE_NAME health check failed after $HEALTH_CHECK_RETRIES attempts."
return 1
}
case "$1" in
start)
start_service
;;
stop)
stop_service
;;
restart)
restart_service
;;
status)
if [ -f "$PID_FILE" ] && kill -0 "$(cat "$PID_FILE")" >/dev/null 2>&1; then
echo "$SERVICE_NAME is running (PID: $(cat "$PID_FILE"))."
check_health
else
echo "$SERVICE_NAME is not running."
exit 1
fi
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
How it works: This comprehensive Bash script manages a service by handling start, stop, restart, and status commands. It uses a PID file to track the service's process ID, allowing for graceful termination (SIGTERM) before resorting to a forceful kill (SIGKILL). Additionally, it includes a configurable HTTP health check, crucial for verifying the service is truly operational after starting or restarting, making it ideal for managing web applications.