VPS Running Slow? How to Find and Fix High CPU Usage
Your site is crawling, SSH takes forever to respond, and your monitoring dashboard has a red CPU graph pinned near 100%. Before you reach for a bigger VPS plan, spend ten minutes finding out what's actually burning the CPU - most of the time it's one runaway process, not a genuine capacity problem.
Symptom: What High CPU Actually Looks Like
It's not always obvious. Sometimes it's a slow website with normal-looking traffic. Sometimes it's SSH sessions that lag on every keystroke. Sometimes cPanel or WHM itself becomes sluggish because the load average has climbed past your core count. If uptime shows a load average of 8.00 on a 2-core VPS, that's your first confirmation - anything sustained above your core count means processes are queued and waiting for CPU time.
uptime
# 14:32:10 up 41 days, 3:12, 2 users, load average: 8.42, 7.90, 6.11
Step 1: Find the Process That's Actually Guilty
Don't guess - measure. SSH in and run:
top -o %CPU
or, if it's installed, htop for a friendlier view. Watch it for 30-60 seconds rather than reacting to the first snapshot - a process spiking briefly during a cron job is different from one pegged at 100% continuously. Note the process name, PID, and which user owns it (that tells you which cPanel account or app is responsible on a shared VPS).
Four culprits show up again and again:
1. PHP-FPM or Apache/LiteSpeed workers
If you see a wall of php-fpm or httpd processes each using real CPU, it's almost always WordPress or another CMS doing expensive work per request - usually uncached pages, a plugin running a heavy query on every load, or a REST API endpoint (wp-json) being hammered by bots. Check your web server's access log for the busiest IPs and URLs in the last few minutes:
tail -n 5000 /usr/local/apache/domlogs/yourdomain.com |
awk '{print $1}' | sort | uniq -c | sort -rn | head -10
A single IP making thousands of requests in a short window is a scraper or a broken script, not a real visitor.
2. MySQL / MariaDB
If mysqld is the top consumer, you likely have a slow, unindexed query running repeatedly. Turn on the slow query log temporarily:
mysql -e "SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 2;"
tail -f /var/lib/mysql/$(hostname)-slow.log
Anything that shows up repeatedly is your target - it's usually a missing index on a large table, or a plugin (analytics, SEO, backups) that scans the whole database on every page load.
3. Cron jobs that overlap
A backup, malware scan, or WP-Cron task that takes longer than its own interval will start stacking copies of itself. Check what's scheduled:
crontab -l
cat /etc/cron.d/* 2>/dev/null
If you see the same script listed as running via both system cron and WordPress's own wp-cron.php, or a backup job scheduled every 15 minutes that actually takes 40 minutes to finish, that's your overlap.
4. Bad bots and vulnerability scanners
Automated scanners hitting wp-login.php, xmlrpc.php, or random plugin paths thousands of times an hour will chew through CPU on the auth and PHP layers even when every request fails. This is common enough that it deserves its own check - if your access log is dominated by requests to login or admin-ajax endpoints from IPs with no referrer, that's what you're looking at.
Fix: Match the Response to the Cause
| Cause | Fix |
|---|---|
| Uncached PHP requests | Enable a page cache (WP Super Cache, LiteSpeed Cache, or server-level cache) and OPcache |
| Slow database query | Add the missing index, or disable/replace the plugin causing it |
| Overlapping cron jobs | Stagger schedules, add a lock file so a job can't start while a previous run is still active |
| Bots hammering login/API endpoints | Rate-limit or block via fail2ban, restrict xmlrpc.php, add a firewall rule (ModSecurity, CSF, or UFW) |
| One account overusing shared resources | Set a CloudLinux CageFS / CPU limit on that account, or move it to its own VPS |
For an immediate, temporary fix while you diagnose properly, you can kill or renice a specific runaway process without rebooting the whole server:
# Lower priority instead of killing, if it's still needed
renice +10 -p <PID>
# Kill it outright if it's clearly stuck
kill -9 <PID>
That buys you breathing room - it doesn't fix the underlying cause, so don't stop there.
Blocking a Bad IP Fast
If a scanner or scraper is the problem, block it at the firewall rather than the application layer - it's cheaper on CPU because the request never reaches PHP:
# CSF (common on cPanel servers)
csf -d 203.0.113.45
# Plain iptables
iptables -A INPUT -s 203.0.113.45 -j DROP
Prevention
- Set up monitoring with alerts. A load-average alert that fires at 70% of your core count gives you time to act before visitors notice anything.
- Cache everything cacheable. Page caching turns most CPU-bound requests into near-free static file serves.
- Audit plugins and cron jobs quarterly. Old, abandoned plugins are consistently the biggest source of unexplained CPU spikes on WordPress VPS setups.
- Rate-limit login and API endpoints. Fail2Ban with a WordPress-specific jail, or a simple
limit_reqzone in Nginx, stops most credential-stuffing bots before they cost you CPU. - Right-size the plan. If your load average is consistently high with no single guilty process, you've outgrown the plan - that's a real scaling problem, not a bug to chase.
Most "my VPS is slow" tickets we see resolve in one of these five buckets. Spend the first ten minutes with top and your access logs before assuming you need more hardware - it's usually cheaper and faster to fix the actual cause.
Frequently asked questions
How do I check CPU usage on my VPS without installing anything?
Run `top` or `uptime` over SSH - both are pre-installed on every Linux distribution. `top -o %CPU` sorts processes by CPU usage so the worst offender is at the top of the list.
Is a load average of 4 bad on my VPS?
It depends on your core count. A load average roughly equal to or below your number of CPU cores is fine. If it's consistently higher than your core count, processes are queuing for CPU time and requests will feel slow.
Will restarting the server fix high CPU usage?
It can give you temporary relief by killing the runaway process, but the underlying cause - a bad plugin, missing index, or bot attack - will come back unless you fix it directly.
How do I stop bots from overloading my VPS?
Block offending IPs at the firewall (CSF or iptables), restrict access to `xmlrpc.php` and `wp-login.php`, and add rate limiting with Fail2Ban or your web server's built-in request-limiting module.
Should I upgrade my VPS plan instead of debugging this?
Only after you've confirmed there's no single runaway process - if `top` consistently shows load spread evenly across many legitimate requests with no bad actor, that's a genuine capacity issue and upgrading makes sense.