File: //opt/cpanel-load.sh
#!/bin/bash
# ==========================================================
# cPanel Server Load & Resource Usage Report (read-only)
# Shows: load average, top CPU/memory processes, top 5 resource-heavy
# cPanel users, MySQL live queries + slow query log, web server
# process usage, and disk I/O. Makes no changes to the server.
#
# Usage: chmod +x server_load_report.sh
# sudo ./server_load_report.sh
# (Must run as root — needed to read process owners correctly)
#
# Output is written both to the screen and to a timestamped
# report file in /var/log/server_load_reports/
# ==========================================================
REPORT_DIR="/var/log/server_load_reports"
mkdir -p "$REPORT_DIR" 2>/dev/null
REPORT="$REPORT_DIR/load_report_$(date +%Y%m%d_%H%M%S).log"
: > "$REPORT"
# ---- Helper functions ----
# Every helper writes directly to screen+file via tee - nothing is
# ever reconstructed later by tailing the report file, so one
# section's output can never leak into another's.
log() {
echo "$1" | tee -a "$REPORT"
}
section() {
{
echo
echo "###############################################################################"
echo "# $1"
echo "###############################################################################"
} | tee -a "$REPORT"
}
run() {
# Runs a command, streams its output (or a fallback message if empty)
# through tee so screen and file always match exactly.
local output
output=$(eval "$1" 2>&1)
if [ -z "$output" ]; then
echo "(no output)" | tee -a "$REPORT"
else
echo "$output" | tee -a "$REPORT"
fi
}
log "SERVER LOAD REPORT - $(date)"
log "Report file: $REPORT"
###############################################################################
# SYSTEM LOAD
###############################################################################
section "SYSTEM LOAD"
run "uptime"
log "CPU cores: $(nproc)"
###############################################################################
# MEMORY
###############################################################################
section "MEMORY USAGE"
run "free -h"
###############################################################################
# TOP PROCESSES BY CPU
###############################################################################
section "TOP 15 PROCESSES BY CPU"
run "ps -eo pid,ppid,user,%cpu,%mem,etime,cmd --sort=-%cpu | head -n 16"
###############################################################################
# TOP PROCESSES BY MEMORY
###############################################################################
section "TOP 15 PROCESSES BY MEMORY"
run "ps -eo pid,ppid,user,%cpu,%mem,etime,cmd --sort=-%mem | head -n 16"
###############################################################################
# RESOURCE USAGE - TOP 5 USERS (cPanel accounts)
###############################################################################
# Each cPanel account maps to a real system user, so aggregating by
# user tells you which cPanel account is actually hammering the server.
section "TOP 5 USERS BY RESOURCE USAGE (cPanel accounts)"
run "ps -eo user,%cpu,%mem --no-headers | awk '{cpu[\$1]+=\$2; mem[\$1]+=\$3; count[\$1]++} END {printf \"%-20s %-10s %-10s %-10s\n\", \"USER\", \"CPU%\", \"MEM%\", \"PROC_COUNT\"; for (u in cpu) printf \"%-20s %-10.1f %-10.1f %-10d\n\", u, cpu[u], mem[u], count[u]}' | (read -r header; echo \"\$header\"; sort -k2 -nr | head -n 5)"
###############################################################################
# MYSQL / MARIADB - LIVE PROCESSLIST
###############################################################################
if command -v mysqladmin &> /dev/null; then
section "TOP MYSQL/MARIADB QUERIES (live)"
run "mysqladmin processlist --verbose 2>/dev/null | head -n 20"
fi
###############################################################################
# MYSQL SLOW QUERY LOG (read-only - reports status, does not enable it)
###############################################################################
section "MYSQL SLOW QUERY LOG (last 50 lines)"
SLOW_LOG=""
if command -v mysql &> /dev/null; then
SLOW_LOG=$(mysql -Nse "SHOW VARIABLES LIKE 'slow_query_log_file';" 2>/dev/null | awk '{print $2}')
fi
if [ -z "$SLOW_LOG" ] || [ ! -f "$SLOW_LOG" ]; then
SLOW_LOG=$(find /var/lib/mysql /var/log -maxdepth 1 -iname "*slow*.log" 2>/dev/null | head -n 1)
fi
if [ -n "$SLOW_LOG" ] && [ -f "$SLOW_LOG" ]; then
log "Slow query log: $SLOW_LOG"
run "tail -n 50 '$SLOW_LOG'"
else
log "Slow query log not found or not enabled on this server."
fi
###############################################################################
# APACHE
###############################################################################
section "APACHE STATUS"
if systemctl is-active --quiet httpd 2>/dev/null; then
log "Apache (httpd): ACTIVE"
run "ps -C httpd -o pid,user,%cpu,%mem,etime,cmd --sort=-%cpu | head -n 25"
else
log "Apache (httpd): NOT ACTIVE"
fi
###############################################################################
# LITESPEED
###############################################################################
section "LITESPEED STATUS"
if systemctl is-active --quiet lsws 2>/dev/null; then
log "LiteSpeed (lsws): ACTIVE"
# LiteSpeed's internal process name varies by build/version (litespeed,
# lshttpd, lsphp) and isn't reliably matched by 'ps -C' (which only
# checks the short kernel comm field). Match the full command line
# instead so master + worker processes are always caught.
run "ps -eo pid,user,%cpu,%mem,etime,cmd --sort=-%cpu | grep -iE 'litespeed|lshttpd' | grep -v grep | head -n 25"
else
log "LiteSpeed (lsws): NOT ACTIVE"
fi
###############################################################################
# LSPHP (per-account PHP workers under LiteSpeed)
###############################################################################
section "TOP 15 LSPHP PROCESSES (per-account PHP workers)"
run "ps -eo pid,user,%cpu,%mem,etime,cmd --sort=-%cpu | grep -i 'lsphp' | grep -v grep | head -n 15"
###############################################################################
# DISK I/O
###############################################################################
section "DISK I/O (5 sec sample)"
if command -v iostat &> /dev/null; then
run "iostat -x 1 2"
else
log "(sysstat not installed - iostat unavailable. This is informational only; the script makes no changes.)"
fi
section "END OF REPORT"
log "Full report saved to: $REPORT"