VMHeaven

Troubleshooting

VPS high CPU usage: find the process and fix the cause

A VPS pinned at 100% CPU: find the process with top and ps, tell real load from steal time and I/O wait, spot a cryptominer, and cap what you keep.

Updated 19 Sept 2026~7 min read

A VPS stuck at 100% CPU gets sluggish everywhere: pages time out, SSH takes seconds to echo a keystroke, cron jobs overlap. The fix is almost always the same sequence. Find out what kind of CPU time is being used, find the process behind it, then decide whether to fix, limit or remove it.

1. Look at the whole machine first

overview
top          # press P to sort by CPU, 1 for per-core view, q to quit
uptime       # load averages for 1, 5 and 15 minutes
nproc        # how many vCPUs you have

The %Cpu(s) line at the top of top tells you more than the process list does. The fields that matter:

  • us — your applications. High us means a process is doing real work. Keep going to step 2.
  • sy / si — the kernel, including network interrupts. Very high values usually mean heavy network traffic, such as a flood of small packets.
  • wa — waiting on disk. The CPU is idle but blocked. That is a storage problem, not a CPU problem. Check it with iostat -x 1 or iotop.
  • st — steal time. Your VM wanted CPU and the host gave it to someone else. Nothing inside your server can fix that. See steal time below.

Load average is relative to nproc: a load of 4 on 4 vCPUs means fully busy, not overloaded. On Linux the number also counts processes waiting on disk, so a high load with low us points at I/O, not CPU.

2. Find the process

top consumers
ps -eo pid,user,%cpu,%mem,etime,comm --sort=-%cpu | head -15

ps shows each process’s average since it started, which can hide a spike. For what is happening right now, trust top, or run pidstat 1 5 from the sysstat package. Once you have a PID, find out what it actually is:

identify PID 1234
sudo ls -l /proc/1234/exe                 # the real binary on disk
tr '\0' ' ' < /proc/1234/cmdline; echo    # full command line
systemctl status 1234                     # which service it belongs to, if any
sudo ls -l /proc/1234/cwd                 # its working directory

3. The usual suspects

php-fpm, apache2, nginx workers

Usually traffic, and often not the human kind. Look at who is hitting you and what they request:

top clients and URLs
sudo awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head
sudo awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head

One IP with tens of thousands of requests, or a flood of POST /xmlrpc.php and /wp-login.php, is a bot. Block it, rate-limit it (limit_req in nginx), or put the login behind an allow-list. If traffic is genuine, the application needs caching or more workers than the CPU can serve.

mysqld / mariadbd

Almost always a few slow queries, repeated. See what is running and turn on the slow log:

MySQL / MariaDB
SHOW FULL PROCESSLIST;
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;

A missing index turns a millisecond lookup into a full table scan, and under load that is the whole CPU.

kswapd0, kworker, ksoftirqd

  • kswapd0 means memory pressure. The kernel is busy freeing RAM, not computing. Check free -h, find the memory hog, and add RAM or a swap file.
  • ksoftirqd means network interrupt load — packet floods, or very high connection rates.
  • kworker is generic kernel work, often storage I/O. Correlate it with wa.

Things that are fine

unattended-upgrade, apt, dnf, man-db and updatedb after boot, or compilers (cc1, cc1plus, rustc) during a build. These are temporary — let them finish.

4. Stop it, or cap it

stop
kill 1234                        # polite: SIGTERM
kill -9 1234                     # only if it ignores SIGTERM
sudo systemctl stop nginx        # a service: stop the unit, or systemd restarts it

Often you want the process to keep running, just without starving everything else:

limit instead of kill
# lower the priority of a running process
sudo renice -n 15 -p 1234

# run a one-off job capped at half a core
sudo systemd-run --scope -p CPUQuota=50% /opt/scripts/report.sh

# cap a service permanently (150% = one and a half cores)
sudo systemctl set-property php8.3-fpm.service CPUQuota=150%

5. When it is a cryptominer

A process you do not recognise, pinned at 100%, is one of the most common signs of a compromised server. Typical signs:

  • The binary lives in /tmp, /var/tmp or /dev/shm, or shows as (deleted).
  • It runs as a service account — www-data, redis, postgres — which tells you which service was the way in.
  • A random or kernel-looking name, and it comes back minutes after you kill it.
  • The CPU is busy but top shows nothing. A preloaded rootkit can hide processes; check /etc/ld.so.preload.
look for evidence
sudo ls -l /proc/*/exe 2>/dev/null | grep -E '/tmp|/dev/shm|/var/tmp|deleted'
sudo ss -tnp state established
sudo ls -la /etc/cron.d /var/spool/cron /var/spool/cron/crontabs 2>/dev/null
systemctl list-timers --all
cat /etc/ld.so.preload 2>/dev/null
sudo find /tmp /var/tmp /dev/shm -type f -perm -u+x 2>/dev/null

When the problem is steal time

If st sits above a few percent while your own usage is modest, the physical host is oversubscribed. Watch it for a minute:

steal time
vmstat 1 30     # watch the 'st' column

Nothing inside the VM fixes that. Contact the provider, or move to a plan with dedicated cores. For single-threaded work — PHP, game servers, Node builds — clock speed matters more than core count. VMHeaven’s Hi-CPU KVM line gives each vCPU a dedicated high-frequency core for exactly that.

Keeping it from happening again

  • Keep a history. A monitoring agent (Netdata, node_exporter, or at least sysstat) tells you what spiked, not just that something did.
  • Keep the OS and every web application patched — most miners arrive through old software.
  • Key-only SSH and a closed firewall. See securing SSH and UFW setup.
  • Cap noisy services with CPUQuota so one runaway cannot take SSH down with it.

Frequently asked

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

Run 'top' and press P to sort by CPU, or 'ps -eo pid,user,%cpu,comm --sort=-%cpu | head'. Then 'ls -l /proc/<PID>/exe' and 'systemctl status <PID>' tell you what the process really is.

What does high steal time (st) mean?

Your VM wanted CPU and the host gave it to another tenant. Nothing inside the server fixes it — contact the provider or move to a plan with dedicated cores.

Is it enough to kill a cryptominer process?

No. Someone had code execution on the server and likely left persistence. Snapshot for evidence, rebuild from a clean image, restore trusted data, rotate credentials and patch the entry point.

More in Troubleshooting

See all