How to Diagnose High CPU Usage on a Linux Web Server
A high-CPU alert is a signal, not a diagnosis. On a Linux web server, slow responses can come from actual CPU saturation, blocked disk I/O, memory pressure, a traffic surge, expensive PHP requests, database queries, background jobs, or a compromised account.
The safest troubleshooting approach is to preserve evidence first, classify the bottleneck, and only then change configuration. Restarting services may restore availability, but it can also erase the clues needed to identify the root cause.
1. Record the incident before changing anything
Capture the time, affected sites, response codes, recent deployments, backup jobs, and monitoring alerts. Then record a small system snapshot:
date
uptime
free -h
df -h
ps aux --sort=-%cpu | head -20
Keep the output with the incident notes. A single screenshot of top is useful, but it does not show what happened before or after the sample.
2. Compare load average with the number of CPUs
uptime
nproc
lscpu
Load average counts runnable tasks and tasks waiting in uninterruptible sleep, so a high load value does not always mean the CPUs are fully busy. Compare the 1-, 5-, and 15-minute load averages with the number of logical CPUs and then inspect CPU states.
A short spike may reflect a deployment or scheduled job. A sustained load above available CPU capacity needs deeper investigation, but high I/O wait can produce a similar symptom.
3. Read CPU states instead of only the headline percentage
Run:
top
vmstat 1
mpstat -P ALL 1
Focus on:
- us: application or user-space work, often PHP, Java, Node.js, or database activity
- sy: kernel work, networking, storage, firewall, or driver overhead
- wa: time waiting for I/O; the server may feel slow even when CPU execution is not the root problem
- st: stolen time on a virtual machine, which can indicate host contention
- si/hi: software or hardware interrupt pressure
Check whether one core is saturated while others are mostly idle. A single-threaded process can bottleneck one CPU without showing 100% across the entire machine.
4. Identify the process and thread consuming resources
ps -eo pid,ppid,user,comm,%cpu,%mem,etime --sort=-%cpu | head -30
pidstat -u -r -d 1
If one process is consistently at the top, identify its parent, user, start time, and open files before terminating it:
ps -fp PID
pstree -p PID
lsof -p PID | head
For a multi-threaded process, inspect threads:
top -H -p PID
ps -L -p PID -o pid,tid,pcpu,comm --sort=-pcpu
Do not kill a process solely because it appears at the top for one sample. A busy process may be doing legitimate work while another bottleneck causes the queue.
5. Check memory pressure, swap, and the OOM killer
free -h
vmstat 1
swapon --show
dmesg -T | grep -i -E 'out of memory|oom|killed process'
Low “free” memory is not automatically a problem because Linux uses memory for cache. Look at available memory, sustained swapping, major page faults, and OOM events. Heavy swap activity can make a server appear CPU-bound while requests wait on storage.
6. Separate disk I/O from CPU pressure
iostat -xz 1
iotop -oPa
High latency, queue depth, or I/O wait may point to backups, log rotation, database writes, malware scans, or storage limits. Correlate the start of the incident with Cron, backup, and snapshot schedules.
Avoid using one universal threshold for every disk. NVMe, local SSD, network storage, and shared cloud volumes have different latency and throughput characteristics. Compare current values with the server’s normal baseline.
7. Determine whether traffic caused the load
Check active connections and the busiest client addresses:
ss -s
ss -ant state established | wc -l
ss -ant | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr | head
Review the web access log for request rate, repeated URLs, status codes, bots, and expensive endpoints. A traffic increase may be legitimate, abusive, or caused by a retry loop.
Do not block an IP until you understand the traffic. Shared proxies, search crawlers, monitoring systems, and customer networks can produce many requests from one address.
8. Inspect Apache or Nginx behavior
Apache
Check the active MPM, worker limits, process count, and server-status if it is safely restricted:
apachectl -M | grep mpm
ps -C apache2 -o pid,pcpu,pmem,cmd --sort=-pcpu
ps -C httpd -o pid,pcpu,pmem,cmd --sort=-pcpu
Nginx
Check worker CPU, connection states, upstream response time, and error logs:
ps -C nginx -o pid,ppid,pcpu,pmem,cmd
nginx -T | less
High web-server CPU may actually originate in an upstream PHP-FPM pool or application server. Compare access-log timing with upstream timing where available.
9. Inspect PHP-FPM pools and slow requests
Confirm which pool serves the affected site, then review its error and slow logs. Useful checks include:
- reaching
pm.max_children - workers stuck on one script or external API
- slow WordPress plugins or uncached requests
- too many separate pools competing for RAM
- long-running Cron or import jobs
A larger pm.max_children is not always better. Every child consumes memory; increasing the limit without capacity planning can turn a CPU issue into swapping or an OOM event.
10. Check MySQL or MariaDB before tuning blindly
mysqladmin processlist
mysql -e 'SHOW FULL PROCESSLIST;'
Enable and review the slow-query log for a representative period. Look for repeated expensive queries, missing indexes, lock waits, temporary tables, and application patterns. Do not run OPTIMIZE TABLE or change buffer sizes as a reflex; first verify that the proposed change addresses the measured bottleneck.
11. Correlate the spike with scheduled activity
systemctl list-timers --all
crontab -l
ls -la /etc/cron.*
Backups, antivirus scans, compression, statistics jobs, certificate tasks, and WordPress Cron can overlap. Stagger heavy jobs and verify that backup software has appropriate CPU and I/O limits.
12. Preserve evidence during recovery
If availability is affected, recovery may require restarting a failed service, scaling resources, limiting a runaway job, or temporarily rate-limiting traffic. Before doing so:
- save process, connection, and log samples
- record the exact command and time
- change one variable at a time
- define a rollback
- monitor after the change
A practical decision tree
- High user CPU: identify application, PHP, or database work.
- High system CPU: inspect kernel, networking, interrupts, and storage behavior.
- High I/O wait: identify the device and process generating I/O.
- High steal time: investigate hypervisor or cloud-host contention.
- Low CPU but slow site: check connections, queues, locks, memory, DNS, and upstream dependencies.
How to verify the fix
Re-run the same measurements under comparable traffic. Confirm that response time, queue length, error rate, load, CPU states, and resource saturation improved together. A lower CPU graph alone is not enough if requests are now queued or failing elsewhere.
If you need a structured review of a hosting environment, see the server administration and infrastructure services offered on this site.