Monitoring the CPU is very important on a Linux server as there may be cases when your applications will need more CPU and can consume everything. You would want to be notified via email in case the CPU usage spikes and you need to do some checks.
For your server to be able to send emails you will need to have configured an SMTP Relay or you should have an email server hosted on the VPS that will send the alarm.
In case you are using an online VPS provider like Hetzner or DigitalOcean having such a script can be very useful as it can catch problems coming from your hosting provider or your app. For more details on Hetzner, you can check this review.
In this article, we will configure a script that will run in crontab every 5 minutes and check to see if the CPU usage is above 80% in case that happens you will be notified via email with the:
- Current Usage
- Top 20 processes that consume high CPU
- Top 10 Processes that consume high CPU using the ps command
- Memory Utilization on the server
In case you are interested to have a web panel that can help you manage your applications and be used as a reverse proxy you can check the bellow course:
Having all of this in an email will help us better understand what is happening on the server we have. To build the script we are going to use shell commands.
Youtube Video With Details
Shell Script To Be Used
Below is the script that we are going to use to help us monitor the CPU usage, this will help you monitor Ubuntu versions like 20.04 or 22.04 or RedHat distros.
#!/bin/bash
cpu_idle=`top -b -n 1 | grep Cpu | awk '{print $8}'|cut -f 1 -d "."`
cpuuse=`expr 100 - $cpu_idle`
date=$(date '+%Y-%m-%d %H:%M:%S')
if [ "$cpuuse" -ge 80 ]; then
SUBJECT="ATTENTION: CPU load is high on $(hostname) at $(date)"
MESSAGE="/tmp/Mail.out"
TO="[email protected]"
echo "CPU current usage is: $cpuuse%" >> $MESSAGE
echo "" >> $MESSAGE
echo "+------------------------------------------------------------------+" >> $MESSAGE
echo "Top 20 processes that consume high CPU" >> $MESSAGE
echo "+------------------------------------------------------------------+" >> $MESSAGE
echo "$(top -bn1 | head -20)" >> $MESSAGE
echo "" >> $MESSAGE
echo "+------------------------------------------------------------------+" >> $MESSAGE
echo "Top 10 Processes which consuming high CPU using the ps command" >> $MESSAGE
echo "+------------------------------------------------------------------+" >> $MESSAGE
echo "$(ps -eo pcpu,pid,user,args | sort -k 1 -r | head -10)" >> $MESSAGE
echo "Memory Utilization on the server" >> $MESSAGE
echo "+------------------------------------------------------------------+" >> $MESSAGE
echo "$(ps_mem)" >> $MESSAGE
mail -s "$SUBJECT" "$TO" < $MESSAGE
rm /tmp/Mail.out
else
echo "$date: Server CPU usage is in under threshold.CPU current usage is: $cpuuse%"
fi
In this script, you need to change the TO with your email and in case you want to change the threshold you put the value you want in the -ge 80 (now is 80).
Activating the CPU Monitoring Script
Install ps_mem
This is a tool that will show you better memory usage per process, to install this tool you do:
Get ps_men:
sudo wget -qO /usr/local/bin/ps_mem https://raw.githubusercontent.com/pixelb/ps_mem/master/ps_mem.py
Make ps_mem executable:
sudo chmod a+x /usr/local/bin/ps_mem
Check the version:
ps_mem --version
If you are receiving an error message /usr/bin/env: ‘python’: No such file or directory, you need to create a symbolic link for /usr/bin/python. In Ubuntu 20.04 or Ubuntu 22.04, only Python 3 is installed by default.
sudo ln -s /usr/bin/python3 /usr/bin/python
Put the Script in Crontab
Create a file with the script under /opt/scripts or in any location you want:
vi /opt/scripts/cpu-alert.sh
Make The File Executable:
sudo chmod a+x /opt/scripts/cpu-alert.sh
Execute the Script:
/opt/scripts/cpu-alert.sh
The output should be on email:
CPU current usage is: 5%
+------------------------------------------------------------------+
Top 20 processes that consume high CPU
+------------------------------------------------------------------+
top - 11:27:12 up 6 days, 4:31, 1 user, load average: 0.08, 0.08, 0.07
Tasks: 204 total, 1 running, 202 sleeping, 0 stopped, 1 zombie
%Cpu(s): 4.1 us, 0.0 sy, 0.0 ni, 95.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 3827.7 total, 556.1 free, 1427.2 used, 1844.4 buff/cache
MiB Swap: 2048.0 total, 1276.7 free, 771.3 used. 1454.0 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1959 clp 20 0 1355012 63248 21020 S 6.2 1.6 127:54.60 beam.smp
1 root 20 0 168072 9940 6204 S 0.0 0.3 2:35.24 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.11 kthreadd
3 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 rcu_gp
4 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 rcu_par_gp
5 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 netns
7 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/0:0H-events_highpri
10 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 mm_percpu_wq
11 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcu_tasks_rude_
12 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcu_tasks_trace
13 root 20 0 0 0 0 S 0.0 0.0 3:41.85 ksoftirqd/0
14 root 20 0 0 0 0 I 0.0 0.0 9:58.13 rcu_sched
15 root rt 0 0 0 0 S 0.0 0.0 0:01.65 migration/0
+------------------------------------------------------------------+
Top 10 Processes that consume high CPU using the ps command
+------------------------------------------------------------------+
%CPU PID USER COMMAND
5.0 596375 btdo php-fpm: pool btdo.uk
3.5 1039 mysql /usr/sbin/mariadbd
1.4 1959 clp /app/erts-12.0/bin/beam.smp -- -root /app -progname erl -- -home /app -- -noshell -s elixir start_cli -mode embedded -setcookie S4WHAF4LH5WHSPWUHPKIVMSFISQKQJVFWEGHSP7YW6MT5LKCNDCA==== -sname plausible -config /app/releases/0.0.1/sys -boot /app/releases/0.0.1/start -boot_var RELEASE_LIB /app/lib -- -extra --no-halt
1.2 772 redis /usr/bin/redis-server 127.0.0.1:6379
0.6 2677 systemd+ /usr/bin/clickhouse-server --config-file=/etc/clickhouse-server/config.xml
0.4 579396 root nginx: worker process
0.3 2071 root node server/server.js
0.1 789 root /usr/bin/containerd
0.1 14 root [rcu_sched]
Memory Utilization on the server
+------------------------------------------------------------------+
Private + Shared = RAM used Program
4.0 KiB + 25.5 KiB = 29.5 KiB erl_child_setup
40.0 KiB + 24.5 KiB = 64.5 KiB epmd
4.0 KiB + 82.5 KiB = 86.5 KiB dumb-init
56.0 KiB + 108.5 KiB = 164.5 KiB inet_gethost (3)
144.0 KiB + 56.5 KiB = 200.5 KiB cron
148.0 KiB + 52.5 KiB = 200.5 KiB atd
320.0 KiB + 0.5 KiB = 320.5 KiB exim4
192.0 KiB + 136.0 KiB = 328.0 KiB agetty (2)
268.0 KiB + 89.5 KiB = 357.5 KiB qemu-ga
340.0 KiB + 104.5 KiB = 444.5 KiB irqbalance
304.0 KiB + 361.5 KiB = 665.5 KiB master
352.0 KiB + 361.0 KiB = 713.0 KiB chronyd (2)
384.0 KiB + 358.5 KiB = 742.5 KiB unattended-upgr
728.0 KiB + 111.5 KiB = 839.5 KiB systemd-udevd
652.0 KiB + 339.5 KiB = 991.5 KiB polkitd
732.0 KiB + 418.5 KiB = 1.1 MiB systemd-logind
920.0 KiB + 243.5 KiB = 1.1 MiB dbus-daemon
800.0 KiB + 391.5 KiB = 1.2 MiB systemd-networkd
4.0 KiB + 1.3 MiB = 1.3 MiB clckhouse-watch
820.0 KiB + 516.5 KiB = 1.3 MiB systemd-resolved
1.4 MiB + 128.5 KiB = 1.5 MiB rsyslogd
328.0 KiB + 1.2 MiB = 1.5 MiB php-fpm7.1
660.0 KiB + 902.5 KiB = 1.5 MiB qmgr
708.0 KiB + 878.5 KiB = 1.5 MiB pickup
1.3 MiB + 458.5 KiB = 1.8 MiB ModemManager
776.0 KiB + 1.2 MiB = 1.9 MiB php-fpm7.2
2.0 MiB + 49.5 KiB = 2.0 MiB proftpd
924.0 KiB + 1.2 MiB = 2.1 MiB php-fpm7.3
1.6 MiB + 533.5 KiB = 2.2 MiB udisksd
2.5 MiB + 1.1 MiB = 3.6 MiB tlsmgr
2.3 MiB + 1.4 MiB = 3.7 MiB bash (2)
3.3 MiB + 992.5 KiB = 4.3 MiB networkd-dispat
4.3 MiB + 73.5 KiB = 4.4 MiB memcached
4.2 MiB + 754.0 KiB = 4.9 MiB docker-proxy (4)
3.6 MiB + 1.5 MiB = 5.1 MiB sshd (2)
5.4 MiB + 5.0 MiB = 10.5 MiB systemd (3)
11.1 MiB + 35.5 KiB = 11.1 MiB clp-agent
12.3 MiB + 1.6 MiB = 13.9 MiB containerd-shim-runc-v2 (5)
14.5 MiB + 2.6 MiB = 17.1 MiB php-fpm8.0
17.4 MiB + 23.5 KiB = 17.4 MiB containerd
18.9 MiB + 170.5 KiB = 19.0 MiB redis-server
17.0 MiB + 3.8 MiB = 20.8 MiB php-fpm8.1 (2)
21.4 MiB + 751.5 KiB = 22.2 MiB multipathd
13.9 MiB + 10.2 MiB = 24.1 MiB systemd-journald
28.5 MiB + 178.5 KiB = 28.6 MiB dockerd
28.9 MiB + 1.3 MiB = 30.2 MiB clickhouse
29.6 MiB + 15.6 MiB = 45.2 MiB postgres (18)
40.1 MiB + 6.0 MiB = 46.0 MiB php-fpm7.4 (2)
47.2 MiB + 8.0 MiB = 55.3 MiB beam.smp
82.3 MiB + 82.5 KiB = 82.4 MiB node
72.0 MiB + 30.0 MiB = 102.0 MiB nginx (8)
807.7 MiB + 398.5 KiB = 808.1 MiB mariadbd
---------------------------------
1.4 GiB
=================================
Activating the script in crontab and creating a log for the run:
#open crontab in edit mode
contab -e
#add the bellow line
*/5 * * * * /bin/bash /opt/scripts/cpu-alert.sh >> /opt/scripts/cpu_check_cron.log 2>&1
The above will create a log under /opt/scripts/cpu_check_cron.log with the output when this is not sending an email.