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.

Published · Updated ~7 min read
On this page

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 machine whose cores are yours alone, and read what a plan actually promises before you assume it does. VMHeaven’s Hi-CPU KVM line puts fewer vCores per plan on AMD EPYC with ECC memory, for work that leans on one or two cores, but it is a shared platform like any VPS and reserves no cores, so steal can show up there too. Reserved cores are a dedicated server.

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 fewer vCores on a less crowded line.

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.

Related articles

All troubleshooting

List processes in Linux with ps aux: every column, plus the ps commands you need

What USER, PID, %CPU, %MEM, VSZ, RSS, STAT and TIME mean in ps aux, how to sort by memory or CPU, find a process by name and show the process tree.

How to make a Minecraft server on a VPS: Java, RAM, ports and a systemd service

Run a Minecraft Java server on a Linux VPS: the right Java version, enough RAM, port 25565 (no router forwarding needed), a systemd service and backups.

Cloudflare error 524 (a timeout occurred): when your origin needs more than 125 seconds

Error 524 means your server took longer than Cloudflare's 125-second limit to answer. Find the slow request, then move long jobs to a queue or a DNS-only host.

Kill a process in Linux: kill, pkill and the right signal

kill PID sends SIGTERM to stop a process cleanly; kill -9 forces it. How to find the PID, kill whatever holds a port, and when kill -9 doesn't work.

grep command: cheat sheet for -i, -v, -r and regex

grep PATTERN FILE prints matching lines. -i ignores case, -v inverts the match, -r recurses a directory. Flags, basic vs extended regex, and grep in a pipeline.

htop explained: CPU bars, sorting, filtering and the top command equivalents

How to read htop: CPU bar colours, load average, memory and steal time, plus the keys to sort, filter, search, tree view and kill, with top equivalents.

CPU cores vs threads: what the difference means, and what a vCPU is

Cores vs threads: a core is a physical unit, a thread is a logical CPU that SMT (Hyper-Threading) adds. What a VPS vCPU is and how to count both on Linux.

Linux CPU info: lscpu, /proc/cpuinfo, nproc and CPU temperature

Get CPU info on Linux with lscpu, /proc/cpuinfo and nproc: model, cores, threads, flags like AVX2, clock speed, and CPU temperature with lm-sensors.

ClamAV on Linux: install it, update the signatures and scan safely

ClamAV Linux guide: install on Ubuntu, Debian or Rocky, keep signatures current with freshclam, scan with clamscan or clamd, and know its limits.