Ravindra BagaleCourses & study guides Track your progress

Guides

High CPU Usage on Linux: How to Find the Cause and Fix It

To fix high CPU usage on Linux, first run uptime and compare the load average with your number of CPUs (nproc). Then find the busy process with top (press P) or ps aux --sort=-%cpu | head. Look inside that process (a slow SQL query, a loop in the code, too many workers), and on AWS T instances check whether you have run out of CPU credits. An unknown process eating 100% CPU can be cryptomining malware.

Quick answer

uptime                              # load average vs...
nproc                               # ...number of CPUs
ps aux --sort=-%cpu | head -6       # top CPU processes
top -H -p <PID>                     # threads inside one process
vmstat 1 5                          # us/sy = compute, wa = disk wait

A live example first

A PHP + MySQL shop site on a t3.small (2 vCPU) becomes very slow every evening.

uptime
 19:42:11 up 12 days,  3:10,  1 user,  load average: 6.84, 6.10, 4.95

A load of almost 7 on 2 CPUs means a lot of work is waiting. Now the top processes:

ps aux --sort=-%cpu | head -4
USER   PID  %CPU %MEM COMMAND
mysql  912  178  22.1 /usr/sbin/mysqld
apache 2210  9.3  2.0 php-fpm: pool www
apache 2214  8.8  2.0 php-fpm: pool www

MySQL is using almost both CPUs. Let us see what it is doing:

SHOW FULL PROCESSLIST;
| 4451 | shop | Query | 12 | Sending data | SELECT * FROM orders WHERE customer_email = 'a@b.com' |

The same query runs again and again, and each one takes 12 seconds. EXPLAIN shows a full table scan of 2 million rows, because customer_email has no index:

CREATE INDEX idx_orders_email ON orders (customer_email);

The query time drops to milliseconds and the load falls below 1. Friends, a bigger instance would have cost more money and still been slow. One index fixed it.

How does Linux show CPU load?

High CPU: find the cause Check load, find the top process, identify it, look inside the app or database, then fix or scale. High CPU: find the cause Slow serverhigh loaduptimevs nproctop / pstop processInside appdump/profileFixcode/DB/scale you Measure, find the process, then look inside it before upgrading

Compare load with cores, find the top process, then look inside it before you scale.

The load average (1, 5 and 15 minutes) counts processes that are running or waiting to run (and, on Linux, those waiting for disk). Compare it with the number of cores. In top, the CPU line splits time into:

Field Meaning If high
us Your programs App or database work
sy The kernel Many small system calls, network
wa Waiting for disk Slow or overloaded storage
st Stolen by the hypervisor Noisy neighbours or T-instance credits

How do I fix it step by step?

  1. Run uptime and nproc.
  2. Run top, press P to sort by CPU, and press 1 to see each core.
  3. Note the PID, user and command of the top process.
  4. Check the wa and st values to rule out disk wait and credit throttling.
  5. Look inside the process with the tool for your stack (below).
  6. Fix the cause (code, query, worker count, cron timing).
  7. Only then decide if you need a bigger or different instance.

Look inside the process, by stack

Java / Spring Boot / Tomcat

top -H -p <PID>                     # busiest thread IDs
printf '%x\n' <TID>                 # convert to hex
jstack <PID> | grep -A15 <hex-tid>  # what that thread is doing

Common causes: an endless loop, heavy logging, or the garbage collector working hard because memory is too small.

Node.js

Node runs your JavaScript on one main thread, so one CPU at 100% usually means a busy loop or a heavy synchronous task (big JSON parsing, crypto, regex). Profile with node --cpu-prof server.js and open the .cpuprofile in Chrome DevTools.

Python

pip install py-spy
sudo py-spy top --pid <PID>

py-spy shows the hottest functions without restarting the app.

PHP-FPM

Many php-fpm processes each using some CPU means heavy traffic or slow pages. Turn on the FPM slow log (request_slowlog_timeout = 5s) to see which scripts are slow.

MySQL and PostgreSQL

  • MySQL: SHOW FULL PROCESSLIST;, the slow query log, and EXPLAIN.
  • PostgreSQL: SELECT pid, now()-query_start, query FROM pg_stat_activity WHERE state='active'; and the pg_stat_statements extension.

Go and .NET

  • Go: add net/http/pprof and run go tool pprof http://localhost:6060/debug/pprof/profile.
  • .NET: dotnet-counters monitor -p <PID> and dotnet-trace collect -p <PID>.

EC2: the CPU credit trap

T2, T3 and T4g instances earn CPU credits when they are idle and spend them in bursts. In standard mode, when the balance reaches zero the instance is held at its baseline (for example 20% per vCPU on a t3.small), and everything feels slow even though top looks low.

  1. Open CloudWatch → EC2 metrics → CPUCreditBalance.
  2. If it falls to 0 at the slow times, that is the cause.
  3. Turn on unlimited mode (you pay for extra credits), or move to an M or C family instance.

Is it malware? A defensive check

Cryptomining malware is the most common thing attackers run on hacked Linux servers, because it turns your CPU into their money. Security firms such as Aqua Security have reported the Kinsing malware hitting servers with exposed Docker APIs and unpatched apps, and then mining cryptocurrency. Warning signs:

  1. A process with a random or fake name (kdevtmpfsi, kworkerds) using almost all CPU.
  2. Its file is in /tmp, /var/tmp or /dev/shm (ls -l /proc/<PID>/exe).
  3. New cron entries you did not add (crontab -l for each user, /etc/cron.d).
  4. Outbound connections to unknown hosts (sudo ss -tnp).

If you see these, do not just kill the process. It usually comes back. Isolate the instance (change its security group), take an EBS snapshot for evidence, find how the attacker got in, and rebuild from a clean image.

Mistakes to avoid

  1. Upgrading the instance first. Find the cause first.
  2. Looking only at %CPU in top. Check wa and st too.
  3. Running heavy cron jobs at peak time. Move reports and backups to quiet hours.

Summary table

Step Command
Load vs cores uptime, nproc
Top process top (P), ps aux --​sort=-%cpu
CPU breakdown vmstat 1, mpstat -P ALL 1
Java threads top -H -p, jstack
Python py-spy top --pid
MySQL SHOW FULL PROCESSLIST, slow log
EC2 credits CloudWatch CPUCreditBalance
Malware signs /proc/PID/exe, crontabs, ss -tnp

Quick quiz: check what you learned

  1. A 2-core server shows a load average of 8. What does it mean?

    1. Work is queueing for CPU
    2. The server is idle now
    3. The disk is fully empty
    4. The network is offline
    Show answer

    A. Load far above the core count means tasks are waiting.

  2. Which command sorts processes by CPU use?

    1. ps aux --sort=-%cpu
    2. ls -la --sort=size
    3. df -h --sort=used
    4. free -m --sort=cpu
    Show answer

    A. ps can sort by the %CPU column.

  3. High "wa" in top points to what?

    1. Waiting for disk I/O
    2. A memory leak in Java
    3. Too many SSH users
    4. A wrong DNS server
    Show answer

    A. wa is time the CPU spends waiting for I/O.

  4. A random-named process runs from /tmp at 100% CPU. What is likely?

    1. A cryptomining malware
    2. A normal kernel thread
    3. The cron daemon itself
    4. A database backup job
    Show answer

    A. Unknown binaries in /tmp using all CPU are a classic miner sign.

Frequently asked questions

What is a normal load average?

Compare it with the number of CPU cores. On a 2-core server, a load around 2 means fully busy. Much higher means work is waiting.

Which command shows the process using the most CPU?

Run top and press P, or ps aux --sort=-%cpu | head -5.

Why is my t3.micro slow at 10 to 20 percent CPU?

T instances have a baseline. When CPU credits run out, the instance is held to that baseline. Check CPUCreditBalance in CloudWatch.

What does high "wa" in top mean?

The CPU is waiting for disk (I/O wait). The real problem is slow storage or heavy disk use, not computation.

How do I know if it is a cryptominer?

Look for unknown processes with random names, running from /tmp or /dev/shm, using nearly all CPU, often with outbound connections to mining pools. Treat it as an incident.

Should I just upgrade the instance?

Only after you know the cause. A bad query or a loop in the code will use a bigger instance too.