Skip to main content
+44 7575 472931[email protected]
HostAccentKnowledge BaseHosting, websites, SEO, and growth

Linux High CPU Usage: Diagnose and Fix a Server at 100%

Linux high CPU usage stuck at 100%? Find the process eating your CPU in 60 seconds, fix the real cause, and keep your VPS fast. Exact commands included.

VPSWebsite Performance
Linux high CPU usage on a VPS diagnosed with top and htop, showing the process pinning the server at 100% in 2026

Your site takes eight seconds to load, SSH feels like wading through wet sand, and top shows every core pinned. Linux high CPU usage looks alarming, but it is almost always one identifiable process, and you can usually name that process in under a minute using tools already installed on your server.

Quick Answer (as of August 2026): Run uptime to read the load average, then top -o %CPU to see which process is burning cycles. If a single process sits above 90%, restart or throttle it. If load is high while the CPU looks idle, your bottleneck is disk I/O or hypervisor steal time instead. On a 2 vCPU server, a sustained load average above 2.0 means requests are queueing.

We resolve 20-30 client server issues every day, and a web server pinned at full CPU is one of the steadiest visitors to that queue. What follows is the same sequence our engineers at Hostaccent work through on a live production box, written so you can run it yourself. Nothing is held back.

One thing before you touch anything: resist the urge to reboot. A reboot wipes out the evidence, and the load usually returns within ten minutes anyway. Diagnose first.

What Linux High CPU Usage Actually Means (and What It Doesn't)

A server at full CPU means one or more processes are asking for more processor time than the machine can supply. On a 2 vCPU box, top showing 200% total is complete saturation, not a bug in the reporting. The number that actually matters is load average divided by core count: a 4-core server at load 4.0 sits exactly at capacity, and at 12.0 it is three times oversubscribed.

Two metrics get confused constantly, so let's separate them.

CPU utilisation is the percentage of processor time being consumed right now. Load average is the number of processes either running or waiting to run, averaged over the last 1, 5 and 15 minutes. On Linux specifically, load average also counts processes stuck in uninterruptible sleep, which usually means they are waiting on disk. That single design detail explains most confused support tickets: you can have a high load average with barely any CPU consumption at all, and the disk is the real problem.

Read the three load numbers as a direction, not a snapshot. If uptime returns load average: 9.20, 5.14, 3.02, the situation is getting worse, because the 1-minute figure is far above the 15-minute figure. Reversed, the spike has already passed and you may be looking at the aftermath rather than the cause.

There is also a threshold question worth answering plainly. Brief spikes to 100% are normal and healthy. A backup runs, a cache rebuilds, someone imports a product catalogue. What signals trouble is sustained saturation: load sitting above your core count for more than a few minutes while page response times climb. If your VPS CPU at 100 percent lasts thirty seconds every hour, ignore it. If it lasts thirty minutes, keep reading.

Pro Tip: Inside top, press 1 to break the summary line into per-core figures. A single core at 100% while three sit idle points at a single-threaded process, which is a completely different fix from all four cores being buried.

Step 1: Find the Process Eating Your CPU in 60 Seconds

Start with three commands, in this order, before you change anything. This sequence is what our team calls the 3-Number Check: your core count, your load average, and your top process percentage. Those three numbers together tell you whether you have a runaway process, a capacity problem, or something that is not a CPU problem at all.

bash
nproc                    # how many cores you actually have
uptime                   # load average: 1min, 5min, 15min
top -o %CPU              # processes sorted by CPU, highest first

If you prefer a friendlier display, htop gives you colour-coded per-core bars and lets you sort with a click. Either works.

What is using my CPU on Linux right now?

For a fast, non-interactive answer that you can paste into a ticket or a note to yourself:

bash
ps aux --sort=-%cpu | head -12

That prints the twelve hungriest processes with their user, PID and command. The user column matters enormously on a web server. A process owned by www-data points to PHP or your web server. One owned by mysql points to the database. One owned by an unfamiliar user account, or with a command name you do not recognise, deserves immediate suspicion.

Once you have a PID, dig into it:

bash
top -H -p 4821           # break the process into its threads
ps -o lstart= -p 4821    # when did this thing actually start?
ls -l /proc/4821/exe     # where does the binary live?
ls -l /proc/4821/cwd     # what directory is it running from?

The lstart output is the one people skip, and it solves cases on its own. A PHP worker that started forty seconds ago is doing normal work. A PHP worker that started six hours ago and is still at 99% CPU is stuck in a loop.

For systems running containers or several services, systemd-cgtop shows consumption grouped by service unit rather than by individual process, which makes an unruly Docker container or a misbehaving app server obvious immediately.

Insider Insight: The single most expensive mistake on this class of ticket is rebooting before capturing top output. The load returns, the evidence is gone, and you start from zero. Take thirty seconds to save what you see: top -b -n 1 > /root/cpu-$(date +%s).txt.

Read the CPU Columns: us, sy, wa and st

The summary line in top tells you what kind of busy your server is, and each kind has a different culprit. A server at 95% us and a server at 95% wa look identical on a monitoring graph and need opposite fixes. Reading this line correctly saves an hour of guessing.

| Column | What it measures | Likely culprit on a web server | First thing to check | |---|---|---|---| | %us | User process time | PHP, MySQL, Node, Python, cron jobs | ps aux --sort=-%cpu | | %sy | Kernel time | Heavy context switching, network stack, filesystem churn | vmstat 1 5 | | %wa | Waiting on I/O | Slow disk, swapping, huge log writes | iostat -xz 1 | | %st | Steal time (VM only) | Noisy neighbours on the host node | vmstat 1 5, then your provider | | %id | Idle | Nothing, this is the good one | Nothing |

High %us is the common case and the easiest to solve, because a named process is doing the work. High %sy usually means the kernel is spending more time switching between tasks than running them, often because far too many worker processes are competing. Check vmstat 1 5 and look at the cs column: context switches in the thousands per second on a small server is a strong signal.

High %wa is the one that produces a high load average with a low CPU reading. Your processes are alive but frozen, waiting for disk. On older SATA-backed servers this is routine. On NVMe SSD storage it usually means something pathological: swap thrashing because memory ran out, or a runaway log file. Check with free -m and iostat -xz 1.

Steal time deserves its own paragraph because it is the only column you genuinely cannot fix from inside the server. %st is processor time the hypervisor gave to someone else on the same physical node. Sustained steal above roughly 10% means your virtual machine is being starved by neighbours, and no amount of tuning inside your VPS will change that. Open a ticket with your provider, quote the number, and ask to be moved.

The Five Causes We See Most, Ranked by Frequency

Linux server high CPU usage on a hosting box is not infinitely varied. In the tickets we handle, the same five causes account for the overwhelming majority, roughly in this order.

1. Runaway PHP-FPM workers. A WordPress plugin with an unbounded loop, a broken cron event firing every page load, or wp-cron.php running on a busy site. Signature: several php-fpm processes at 90%+, all owned by www-data, all started minutes apart. This is also the number one cause behind slow page delivery, which we covered separately in the guide to fixing high TTFB in WordPress.

2. Unindexed or slow database queries. One mysqld process at 100% while everything else idles. Usually a query scanning a table that grew past the point where a missing index stopped being harmless, or a plugin writing to wp_options with autoload enabled on a 40MB row set.

3. Bot traffic and login brute-force. Scrapers, aggressive crawlers and automated login attempts hitting wp-login.php or xmlrpc.php thousands of times an hour. Each request spawns a PHP process, so a modest attack turns into full saturation quickly. Cloudflare's overview of bot traffic is a good primer on how much of the modern web is automated.

4. Overlapping cron and backup jobs. Three sites all scheduled at midnight, each compressing a database dump, all on the same 2-core server. The fix is scheduling, not hardware.

5. Compromise, usually a crypto miner. A process with a random name at a steady 100%, often running from /tmp or /dev/shm, and often restarting itself after you kill it. Rarer than the others, but the one that costs the most if missed.

According to Hostaccent's support-queue data, Linux server issues account for roughly 25% of the tickets we handle each month, with WordPress problems at about 30% and brute-force or malware incidents at 25%. Those three categories overlap heavily on exactly this symptom, which is why a pinned CPU so often turns out to be a WordPress problem wearing a Linux costume.

Fix Each Cause: Exact Commands and File Paths

Stabilise first, then fix properly

If the site is down right now, buy yourself breathing room before you diagnose further. Lower the priority of the offending process rather than killing it outright:

bash
renice +19 -p 4821                          # deprioritise, keeps it alive
cpulimit -p 4821 -l 50                      # hard cap at 50% of one core
systemctl restart php8.3-fpm                # clears all stuck workers

Restarting PHP-FPM drops in-flight requests but takes about a second and clears every stuck worker at once. On a live store mid-checkout, prefer renice first.

Runaway PHP-FPM workers

Turn on the slow log so the loop identifies itself. Edit /etc/php/8.3/fpm/pool.d/www.conf:

bash
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/www-slow.log
pm = dynamic
pm.max_children = 20
pm.max_requests = 500

pm.max_requests recycles workers after 500 requests, which contains slow memory leaks automatically. Sizing pm.max_children correctly is where most servers go wrong: set it too high and the server accepts more concurrent work than it can finish, which is how %sy climbs. Divide available memory by average worker size, and check the official PHP-FPM configuration reference before guessing. Our walkthrough on Nginx and PHP-FPM performance tuning covers the sizing maths in detail.

For WordPress specifically, disable the pseudo-cron in wp-config.php and run a real one:

bash
define('DISABLE_WP_CRON', true);
*/5 * * * * curl -s https://yoursite.com/wp-cron.php?doing_wp_cron >/dev/null

Slow MySQL and MariaDB queries

Find the offender live:

sql
SHOW FULL PROCESSLIST;
KILL QUERY 8823;

Then enable the slow query log in /etc/mysql/mariadb.conf.d/50-server.cnf:

bash
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1

Give it an hour of real traffic, then summarise with mysqldumpslow -s t /var/log/mysql/slow.log | head -20. The same two or three queries will dominate. Add the missing index and the CPU graph flattens. Raising innodb_buffer_pool_size helps genuinely memory-starved databases, but it will not rescue a query with no index behind it. The MariaDB documentation has the current syntax for both.

Live site and no time to experiment? Our engineers fix this exact class of problem for a small one-time fee, and you see the exact quote before anyone touches your server. Hosted with Hostaccent? Then issues like this are simply covered by support, at no extra cost. Have an engineer fix it

Bot traffic and brute force

Identify the source before blocking anything:

bash
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

One IP with 40,000 requests is your answer. Rate limiting at the web server layer is the durable fix, using the Nginx limit_req module, and we walk through working configs in the guide to Nginx rate limiting and basic bot protection.

Overlapping cron and backups

Stagger schedules, and wrap heavy jobs so they yield:

bash
ionice -c3 nice -n19 /usr/local/bin/backup.sh

That runs the job at idle disk and CPU priority, so it finishes later but never starves visitors. Our rsync and cron backup automation guide covers scheduling that does not collide.

An unexplained process at 100%

Check where it lives and whether it reinstalls itself:

bash
ls -la /proc/4821/exe
crontab -l; ls /etc/cron.d/
systemctl list-unit-files | grep enabled

If it restarts after kill -9, you have a persistence mechanism, and cleaning the binary alone will not help. Rebuild from a known-good backup and then close the entry point using a proper Linux VPS security baseline.

Confirm the Fix and Stop It Coming Back

Confirmation takes two checks, not one. Load average should fall below your core count and stay there for at least 15 minutes, and your slowest real page should return to its normal response time. A load average that drops the instant you restart a service, then climbs again over the next hour, means you cleared the symptom and left the cause running.

Install historical monitoring so the next incident is not a guessing game:

bash
apt install sysstat
systemctl enable --now sysstat
sar -u                    # CPU by 10-minute interval, today
sar -u -f /var/log/sysstat/sa07

Being able to say "this started at 03:10 every night" narrows the search from everything to one cron window in a single glance.

Three habits prevent most repeat incidents. Cap what each service is allowed to consume, using pm.max_children for PHP and CPUQuota= in a systemd unit for application servers, so one bad deploy cannot take the whole machine down. Keep the slow query log enabled permanently at long_query_time = 1. Stagger every scheduled job by at least ten minutes. If you run Node services, process managers handle restart limits well, which we cover in the guide to deploying a Node.js app with PM2 and Nginx.

One honest caveat about upgrading. More cores genuinely fix a capacity problem, meaning a server that is legitimately busy with real traffic during normal hours. More cores do not fix an infinite loop, and they do not fix a missing database index. They just give the bug more room to run while your invoice goes up. Diagnose first, then decide.

Pro Tip: Alert on sustained load, not on spikes. A threshold of "load average above core count for 5 continuous minutes" catches real incidents and ignores the harmless one-second peaks that make spike-based alerting so easy to start ignoring.

Your Next Step: Fixed It, or Still Stuck?

Path A, you fixed it. You traced the Linux high CPU usage to a real process and load is back under your core count. Keep it there with one habit: a sar baseline plus an alert on sustained load, so the next spike reaches you before it reaches your visitors. Worth saying plainly, though: on a well-managed host, this class of problem is support's job, not yours. You can start on the Basic Linux VPS at $7.99/mo, which renews at the same $7.99/mo, with full root access, free 30 Gbps DDoS filtering, a 99.9% uptime guarantee, a 30-day money-back guarantee, and 24/7 help from Hostaccent's own engineers. Fit check: no commercial control panel licence is bundled, so budget separately if you want cPanel or Plesk.

Path B, still stuck. Open a ticket and you get the exact quote before any work begins.

Frequently Asked Questions About Linux High CPU Usage

What Causes Linux High CPU Usage on a VPS?

On web servers, five causes cover almost everything: runaway PHP-FPM workers from a looping plugin, slow database queries missing an index, bot or brute-force traffic spawning hundreds of PHP processes, overlapping backup and cron jobs, and occasionally a crypto miner from a compromised site. Run ps aux --sort=-%cpu | head -12 and the owning user of the top process usually tells you which of the five you are dealing with within seconds.

Why is my load average high but CPU usage low?

Because Linux counts processes waiting on disk toward the load average, not just processes using the CPU. Those processes sit in uninterruptible sleep, alive but frozen, waiting for storage to respond. Check the %wa column in top and run iostat -xz 1. If wait time is high, your bottleneck is disk or memory pressure causing swap, not the processor, and adding cores will change nothing at all.

Is it safe to kill a process that is using 100% CPU?

Usually yes, with one caveat. Send kill -TERM <PID> first so the process shuts down cleanly, and only escalate to kill -9 if it ignores you. Never kill -9 a database mid-write, because you risk table corruption. Use KILL QUERY <id> inside MySQL or MariaDB instead, which stops the offending query while leaving the server running normally and your data intact.

How much CPU usage is normal on a Linux web server?

Short spikes to 100% are completely normal during backups, cache rebuilds and traffic bursts. The healthy steady state is a load average below your core count, so under 2.0 on a 2 vCPU server and under 4.0 on a 4-core one. Sustained saturation lasting more than a few minutes, especially when page response times climb alongside it, is what warrants investigation. Idle percentages above 20% are comfortable.

Should I upgrade my VPS if the CPU keeps hitting 100 percent?

Only after you have identified the cause. Across the 10,000+ websites Hostaccent has launched and the 4,000+ migrations completed since 2012, a repeating pattern shows up: roughly half of "we need a bigger server" tickets turn out to be one plugin, one query, or one cron job. Upgrading a machine with an infinite loop just buys the loop more room. Diagnose first, then upgrade if the traffic is genuinely real.

What is steal time and can I do anything about it?

Steal time, the %st column in top, is CPU time the hypervisor allocated to other virtual machines on the same physical host. Sustained steal above roughly 10% means your VPS is being starved by neighbours, and nothing you configure inside your own server will change it. Capture the vmstat 1 5 output showing the figure, send it to your provider, and ask to be migrated to a less contended node.

Reviewed by

HostAccent Editorial Team

Our support team handles 20–30 issues like this every day.

Last updated

Aug 8, 2026

Tom HargreavesVPS & Infrastructure Writer

Tom specialises in VPS deployment, server performance tuning, and Linux infrastructure. He has configured hundreds of production servers across Europe and North America.

Discussion

Have a question or tip about this topic? Share it below — your comment will appear after review.

Your email stays private and is only used for moderation.

Write for the Community

Have a tutorial, tip, or insight to share? Get published on the HostAccent Blog with your name, bio, and website link.

Become a Contributor

Need a faster setup for this workflow?