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?
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?
- Run
uptimeandnproc. - Run
top, press P to sort by CPU, and press 1 to see each core. - Note the PID, user and command of the top process.
- Check the
waandstvalues to rule out disk wait and credit throttling. - Look inside the process with the tool for your stack (below).
- Fix the cause (code, query, worker count, cron timing).
- 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, andEXPLAIN. - PostgreSQL:
SELECT pid, now()-query_start, query FROM pg_stat_activity WHERE state='active';and thepg_stat_statementsextension.
Go and .NET
- Go: add
net/http/pprofand rungo tool pprof http://localhost:6060/debug/pprof/profile. - .NET:
dotnet-counters monitor -p <PID>anddotnet-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.
- Open CloudWatch → EC2 metrics → CPUCreditBalance.
- If it falls to 0 at the slow times, that is the cause.
- 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:
- A process with a random or fake name (
kdevtmpfsi,kworkerds) using almost all CPU. - Its file is in
/tmp,/var/tmpor/dev/shm(ls -l /proc/<PID>/exe). - New cron entries you did not add (
crontab -lfor each user,/etc/cron.d). - 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
- Upgrading the instance first. Find the cause first.
- Looking only at
%CPUin top. Checkwaandsttoo. - 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
A 2-core server shows a load average of 8. What does it mean?
- Work is queueing for CPU
- The server is idle now
- The disk is fully empty
- The network is offline
Show answer
A. Load far above the core count means tasks are waiting.
Which command sorts processes by CPU use?
- ps aux --sort=-%cpu
- ls -la --sort=size
- df -h --sort=used
- free -m --sort=cpu
Show answer
A. ps can sort by the %CPU column.
High "wa" in top points to what?
- Waiting for disk I/O
- A memory leak in Java
- Too many SSH users
- A wrong DNS server
Show answer
A. wa is time the CPU spends waiting for I/O.
A random-named process runs from /tmp at 100% CPU. What is likely?
- A cryptomining malware
- A normal kernel thread
- The cron daemon itself
- A database backup job
Show answer
A. Unknown binaries in /tmp using all CPU are a classic miner sign.
Learn it properly
These pages go deeper, with labs:
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.