Skip to content

Monitor CPU and RAM Usage

monitorsystemawk
6 min read

Quick Answer

The top command in batch mode outputs system metrics non-interactively. Running top -bn1 produces a one-shot snapshot: -b enables batch mode (no terminal control codes), -n1 takes a single sample. The CPU idle percentage on the third line of output reveals load. The free command reports memory in bytes; free -m outputs megabytes. Piping both through awk extracts the specific fields needed for comparison. Without resource monitoring, a runaway process can consume 100% CPU or fill RAM with no warning until the server becomes unresponsive or the OOM killer starts terminating processes. Knowing when CPU stays above 90% for sustained periods distinguishes a traffic spike from a runaway process or a background job that never finished. top and free both come from the procps package that every Linux distribution installs by default. Schedule with cron every 5 minutes to catch problems before users notice: */5 * * * * /home/user/cpucheck.sh.

I started a long job on my own machine, walked off to do something else, and came back a few hours later to a desktop so sluggish the mouse cursor stuttered when I moved it. One process had pinned every core of the i7 and was steadily eating into 64 GB of RAM, and it had been doing it, uncontested, the entire time I was away. By the time the machine felt slow under my hands, the damage — a wasted afternoon, a swap-thrashed box I had to coax back to responsiveness — was already done.

The thing that gets you isn't the runaway process; those happen. It's that a busy machine and a healthy machine feel identical until you're sitting in front of one. CPU and memory pressure leave no trace unless something is sampling them and writing it down. So I built the check below to take a one-shot reading of CPU and RAM, compare each against a threshold, and log a warning the moment either crosses it. One detail worth carrying with you: when the kernel's OOM killer finally reaps a process under memory pressure, it exits with code 137 — that's 128 plus signal 9 — so a 137 in your logs is RAM exhaustion's fingerprint, not a bug in your code. Catching that an hour in, instead of an afternoon later, is the whole reason this runs now.

The monitor I built after my own machine seized up

Paste this into monitor.sh. Adjust THRESHOLD and LOG_FILE if you want a different cutoff or log path.

bash
#!/bin/bash CHECK="✓" CROSS="✗" # --- Configuration --- THRESHOLD=80 # Alert when usage exceeds this % LOG_FILE="/var/log/resource-monitor.log" DATE=$(date '+%Y-%m-%d %H:%M:%S') # --- CPU Usage --- CPU=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1 | cut -d',' -f1 | xargs printf "%.0f") # --- RAM Usage --- RAM=$(free | awk '/Mem:/ {printf "%.0f", $3/$2*100}') echo "[$DATE] CPU: ${CPU}% | RAM: ${RAM}%" # --- CPU Alert --- if [ "$CPU" -gt "$THRESHOLD" ]; then echo "$CROSS [$DATE] WARNING: CPU at ${CPU}% (threshold: ${THRESHOLD}%)" | tee -a "$LOG_FILE" else echo "$CHECK CPU OK: ${CPU}%" fi # --- RAM Alert --- if [ "$RAM" -gt "$THRESHOLD" ]; then echo "$CROSS [$DATE] WARNING: RAM at ${RAM}% (threshold: ${THRESHOLD}%)" | tee -a "$LOG_FILE" else echo "$CHECK RAM OK: ${RAM}%" fi

What each line is actually reading

Line / pieceWhat it does
THRESHOLD=80The percentage at which a warning fires.
top -bn1Runs top in batch mode for one iteration (non-interactive).
awk '{print $2}'Extracts the CPU usage column from top output.
free | awk '/Mem:/ ...'Parses free output to calculate RAM used / total × 100.
tee -a "$LOG_FILE"Prints to stdout and appends to the log file.
$DATETimestamp so every log entry is traceable.

Getting it logging on your box

Step 1 — Create the file

bash
nano monitor.sh

Paste the script above, then press Ctrl+X → Y → Enter to save.

Step 2 — Set your threshold

ThresholdBest for
70%Early warning on production — gives time to investigate before impact
80%General-purpose default — recommended for most VPS and cloud instances
90%Last-chance alert — for workloads with legitimate sustained high usage

Edit THRESHOLD=80 at the top of the script to match your environment.

Step 3 — Make it executable and test it

bash
chmod +x monitor.sh ./monitor.sh

You should see output like:

text
[2026-06-03 14:22:01] CPU: 12% | RAM: 54% ✓ CPU OK: 12% ✓ RAM OK: 54%

If CPU or RAM is above the threshold, the warning line fires and appends to LOG_FILE. Check the log after a test run:

bash
cat /var/log/resource-monitor.log

Step 4 — Schedule with cron

The script only helps if it runs automatically. Open your crontab:

bash
crontab -e

Add one of these lines:

bash
# Check every 5 minutes */5 * * * * /home/user/monitor.sh >> /var/log/monitor-cron.log 2>&1 # Check every hour 0 * * * * /home/user/monitor.sh >> /var/log/monitor-cron.log 2>&1 # Check every minute (high-stakes production) * * * * * /home/user/monitor.sh >> /var/log/monitor-cron.log 2>&1

Always redirect cron output to a log

Cron runs silently. Without >> /var/log/monitor-cron.log 2>&1 on your cron line, you will never see warnings from a threshold breach. Redirect both stdout and stderr so every run leaves a trace.

Versions for email, per-process, and graphing

Send an email when CPU or RAM spikes

Same CPU/RAM probes as above, plus a single email whenever either crosses your threshold — useful instead of silently appending log lines. Requires the mail command (typically mailutils on Debian/Ubuntu); see our bash email alert snippet for install tips, SMTP, and Gmail.

bash
#!/bin/bash # --- Configuration --- THRESHOLD=80 EMAIL="you@example.com" # ← recipient CPU=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1 | cut -d',' -f1 | xargs printf "%.0f") RAM=$(free | awk '/Mem:/ {printf "%.0f", $3/$2*100}') HOST=$(hostname) if [ "$CPU" -gt "$THRESHOLD" ] || [ "$RAM" -gt "$THRESHOLD" ]; then TOP5=$(ps aux --sort=-"%cpu" | head -n 6) { echo "Hostname: $HOST" echo "" echo "CPU usage (approx.): ${CPU}%" echo "RAM usage (approx.): ${RAM}%" echo "" echo "Threshold: ${THRESHOLD}% — at least one metric exceeded." echo "" echo "Top 5 processes by CPU (includes ps header row):" echo "${TOP5}" } | mail -s "⚠ Resource alert on $HOST — CPU ${CPU}% | RAM ${RAM}%" "$EMAIL" fi

Monitor a specific process by name

Pass a substring that appears in ps aux COMMAND column (same idea as grepping logs). Matches every PID whose line contains that substring, sums aggregate %CPU and %MEM, and warns when either crosses a per-watch threshold.

bash
#!/bin/bash # Usage: ./monitor-process.sh nginx 50 # Sums ps %CPU/%MEM for rows whose COMMAND matches $NAME; excludes grep. NAME="$1" PROC_THRESHOLD="${2:-50}" if [ -z "$NAME" ]; then echo "Usage: $0 [threshold-percent]" exit 1 fi MATCHES=$(ps aux | grep "$NAME" | grep -v grep) if [ -z "$MATCHES" ]; then echo "No process matched '$NAME'." exit 0 fi CPU_SUM=$(echo "$MATCHES" | awk '{s += $3} END { printf "%.0f", s+0 }') RAM_SUM=$(echo "$MATCHES" | awk '{s += $4} END { printf "%.0f", s+0 }') echo "[$(date '+%Y-%m-%d %H:%M:%S')] $NAME: CPU agg ${CPU_SUM}% | MEM agg ${RAM_SUM}% (threshold ${PROC_THRESHOLD}%)" if [ "${CPU_SUM:-0}" -gt "$PROC_THRESHOLD" ] || [ "${RAM_SUM:-0}" -gt "$PROC_THRESHOLD" ]; then echo "⚠ WARNING: aggregate CPU or MEM above threshold for '$NAME'" fi

Append JSON log lines for graphing

Every run prints one newline-delimited JSON object and appends it to .jsonl — one record per cron tick — so ingestion tools, log aggregators, or a tiny Python/awk script can build charts.

bash
#!/bin/bash LOG_FILE="/var/log/resource-metrics.jsonl" CPU=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1 | cut -d',' -f1 | xargs printf "%.0f") RAM=$(free | awk '/Mem:/ {printf "%.0f", $3/$2*100}') TS=$(date -u +'%Y-%m-%dT%H:%M:%SZ') LINE=$(printf '{"timestamp":"%s","cpu":%s,"ram":%s}\n' "$TS" "$CPU" "$RAM") echo "$LINE" >> "$LOG_FILE"

Where the parsing falls apart

top output format differs between distros

The awk column for CPU usage is $2 on Ubuntu but may be different on CentOS/RHEL or Alpine. Always test your parsing command on the actual target system before scheduling with cron. Run: top -bn1 | grep "Cpu(s)" and count the columns manually.

The script logs warnings but nobody reads the log

Writing to a log file is pointless if nobody checks it. Combine this with the Send Email Alerts from Bash snippet to get notified when thresholds trip. Or pipe cron output to your inbox. When sustained resource pressure is causing service crashes, the restart service watchdog handles automatic recovery between monitoring cycles. A service the kernel kills under memory pressure exits with code 137 — 128 plus signal 9 (SIGKILL), the OOM killer's signature — so a 137 in your logs points to RAM exhaustion, not a code bug.

RAM percentage includes cache/buffers on older systems

On older kernels, free may not show "available" memory correctly. The $3/$2 calculation can overstate usage because Linux aggressively caches disk reads in RAM. On modern Ubuntu (18.04+), this is handled correctly.

Frequently Asked Questions

How do I check CPU usage in bash?

Use top in batch mode: top -bn1 | grep 'Cpu(s)'. For idle as a number, pipe through awk: top -bn1 | grep 'Cpu(s)' | awk '{print $8}'

How do I check RAM usage in Linux?

Use free: free -h is human-readable. In a script: free | awk '/Mem:/ {printf "%.0f", $3/$2*100}' gives RAM usage as a percentage.

How do I get an alert when CPU is too high?

Capture CPU use in a script, compare to a threshold with an if, then schedule the script with cron every minute or hour (or use email from the variations above).

What is a normal CPU usage percentage on a Linux server?

Under 70% is generally healthy for production. Sustained 80–90%+ usually means something worth inspecting with top or htop.

How do I find which process is using the most CPU?

Run: ps aux --sort=-%cpu | head -10 for the top 10. Or run top interactively and press P to sort by CPU.

Part of the Server Monitoring collection

BashSnippets logo

Written by Anguishe

Creator of BashSnippets.xyz

bashsnippets.xyz/about

Run this script on a real Linux server

Get $200 free credit — DigitalOcean

Get $200 Free →

Affiliate link · we earn a commission

Need a domain for your next project?

Register with Namecheap — free WHOIS privacy included

Check Domain Prices →

Affiliate link · we earn a commission

PAID RESOURCE — $9

The Production Bash Toolkit

6 scripts + shared library + 52-page field guide. The production layer the free snippets don't cover.

Get the Toolkit →

Related Snippets

Frequently Asked Questions

faq — snippet

How do I run this script?

Save as cpucheck.sh, set CPU_THRESHOLD and RAM_THRESHOLD, run chmod +x cpucheck.sh, then execute ./cpucheck.sh.

faq — snippet

Does this work on macOS?

Partially. top works on macOS but output format differs. Use vm_stat instead of free for RAM on macOS.

faq — snippet

How do I check CPU usage in bash?

Run top -bn1 and pipe through awk to extract the idle percentage. Subtract from 100 to get CPU usage.

faq — snippet

How do I get an alert when CPU is too high?

Compare extracted CPU percentage against a threshold in an if statement, then pipe the alert to mail or a webhook.