Why Is My Linux Server CPU at 100%? Find the Culprit
A practical triage path for finding runaway processes, separating CPU pressure from I/O wait, and restoring a slow Linux server safely.
Your Linux server is slow, requests are timing out, and top shows a process pinning the CPU at 100%. Before killing it, confirm whether the machine is actually CPU-bound. High load can also come from tasks blocked on storage, virtual machine steal time, or several scheduled jobs competing for the same resources.
Confirm whether the CPU is actually saturated
Start with overall utilization, the number of logical CPUs, and the load averages. On a four-core server, one single-threaded process can display 100% while three cores remain available. A load average near four may be manageable if tasks are running normally, while the same load can feel severe when several tasks are blocked in D state waiting for storage.
top -b -n 1
uptime
nproc
Compare the load averages with the logical CPU count. In the top output, inspect user, system, I/O wait, idle, and steal percentages.
Next, collect interval samples. The first line from vmstat represents averages since boot, so base your diagnosis on the later lines. mpstat is supplied by the sysstat package and shows whether pressure is spread across all CPUs or concentrated on one.
vmstat 1 10
mpstat -P ALL 1 5
Run as root if you need complete process visibility. Install the sysstat package if mpstat is unavailable.
| Metric | What it indicates | What to investigate |
|---|---|---|
| us | Time executing application code | Application workers, compression, scans, queries, or unexpected programs |
| sy | Time executing kernel code | Heavy networking, filesystem activity, drivers, or excessive system calls |
| wa | Time CPUs are idle while work waits for I/O | Disk latency, storage saturation, backups, scans, or blocked mounts |
| st | CPU time taken by the hypervisor | An overloaded virtualization host or an undersized VM plan |
| r | Tasks ready to run | Sustained values above the CPU count indicate a CPU run queue |
| b | Tasks blocked on I/O | Storage, filesystem, network mount, or device problems |
Find the process and thread consuming CPU
List processes by CPU use with their parent, state, priority, processor, age, and full command line. Repeat the sample several times. A process that appears once and disappears may be harmless, while the same process remaining at the top for minutes deserves investigation.
ps -eo pid,ppid,user,stat,ni,psr,%cpu,%mem,etimes,comm,args --sort=-%cpu | head -n 25
pidstat -u -r -d -p ALL 1 5
pidstat separates CPU, memory faults, and disk activity for each process across repeated intervals.
A multi-threaded application can hide the hot worker behind one process name. Inspect its individual threads, then verify the executable and command line from /proc. This is especially useful when a generic name such as python3, java, or php-fpm appears at the top.
ps -L -p 12345 -o pid,tid,psr,stat,%cpu,%mem,comm --sort=-%cpu
readlink -f /proc/12345/exe
tr '\0' ' ' < /proc/12345/cmdline
lsof -p 12345 | head -n 40
Replace 12345 with the process ID. The executable, arguments, and open files help distinguish expected work from a broken worker or unknown program.
Find the owning systemd unit or container before taking action. A supervisor may immediately restart a killed worker, creating a loop that makes the incident worse.
systemctl status --no-pager 12345
cat /proc/12345/cgroup
docker stats --no-stream
The Docker command is relevant only on hosts running Docker. The cgroup path often identifies the systemd service or container responsible for the process.
Separate CPU pressure from disk I/O wait
If the server feels saturated but overall CPU use is moderate, look for processes in D state. These tasks are waiting inside the kernel and cannot handle signals until the underlying operation returns. Killing the process may therefore have no immediate effect.
ps -eo state,pid,ppid,user,wchan:32,%cpu,%mem,comm,args | awk '$1 ~ /^D/'
iostat -xz 1 5
Use the later iostat samples. Check latency and queue depth together rather than treating disk utilization as a complete diagnosis.
| I/O metric | Meaning | Warning pattern |
|---|---|---|
| await | Average request completion time | A sustained increase that matches application slowdown |
| aqu-sz | Average queued requests | A growing queue while latency rises |
| %util | Time the device reports active | High activity combined with elevated latency and queue depth |
| r/s and w/s | Read and write operations per second | A sudden change that lines up with a backup, scan, or workload spike |
Do not rely on %util alone for SSD and NVMe devices, which can process requests concurrently. Elevated await, a persistent queue, blocked tasks, and visible application latency provide stronger evidence. If storage looks healthy, check st in vmstat next. High steal time means the hypervisor is withholding CPU, which cannot be fixed by terminating a guest process.
Reduce the load without killing the wrong process
First decide whether the process is expected. Compression, account backups, malware scans, package upgrades, database maintenance, and log rotation can all be legitimate. When two scheduled jobs overlap, lowering their CPU and I/O priority is often safer than stopping them halfway through.
target_pid=12345
renice -n 10 -p "$target_pid"
ionice -c 2 -n 7 -p "$target_pid"
Replace 12345 with the process ID. This reduces contention but does not impose a hard resource limit.
If a service is malfunctioning, use its service manager or vendor control panel to stop it cleanly. When no supported control exists and termination is necessary, send SIGTERM, wait, and check whether it exited. Reserve SIGKILL for a confirmed process that cannot be stopped safely by another method.
target_pid=12345
kill -TERM "$target_pid"
sleep 5
ps -p "$target_pid" -o pid,stat,etime,cmd
If the process remains in D state, resolve the storage or mount problem instead of repeatedly sending signals.
For cPanel servers, avoid scheduling account backups and full malware scans in the same window. In the supplied incident, pkgacct, pigz, and cpbackup_transporter were processing the account serving bashpilot.com while Imunify360 workers were scanning concurrently. MySQL remained at a steady background level. The appropriate fix is to let one maintenance workload complete, reduce its priority if service latency is unacceptable, and move future backup and scan schedules apart.
Verify that the server recovered
Repeat the same measurements used during diagnosis. CPU idle time should recover, the run queue and blocked-task count should fall, disk latency should return toward its normal baseline, and the offending process should either complete or remain at its reduced priority. Also test the affected application, because improved system metrics do not prove that user-facing requests are healthy.
vmstat 1 10
iostat -xz 1 5
pidstat -u -d -p ALL 1 5
uptime
Load averages decay over time, so the one-minute value may remain elevated briefly after the immediate pressure has ended.
Check the service endpoint, error logs, and request latency as well. If CPU pressure returns at the same time each day, compare the recurrence with cron jobs, systemd timers, backup schedules, security scans, and database maintenance. A recurring pattern is usually easier to fix at the scheduler than at the process level.
After establishing this baseline manually, BashPilot can repeat the same read-only triage across supported Linux servers using vetted operations. If a destructive follow-up is proposed, it requires explicit approval, and any write is followed by verification.
See how BashPilot separates CPU use from I/O pressure
The server is not currently at 100% CPU. The immediate bottleneck is disk contention caused by overlapping maintenance work. A cPanel backup for the account serving bashpilot.com is running at the same time as an Imunify360 malware scan. The backup process is blocked in D state while waiting for storage.
- cPanel backup:
pkgacctis using 68.6% CPU,pigz28.3%, andcpbackup_transporter27.9%. - Imunify360 scan: the scan runner is using 35% CPU,
imunify360-pam36.7%, andrustbolit26%. - MySQL: steady at 13% CPU and 682 MB RSS, consistent with background database activity rather than the main bottleneck.
| Metric | Observed value |
|---|---|
| Total CPU utilization | 34% across 4 cores |
| Load averages | 2.17 / 1.86 / 1.96 |
| I/O wait | 12% |
| Disk utilization | 59% |
| Disk queue depth | 3.07 |
| Memory | 4.9 GB used of 15.8 GB |
| Swap | 2 GB used of 7.9 GB |
Try this on one of your own servers.
Start free trialPrevent the next CPU or I/O spike
- Separate backup, compression, malware scanning, package maintenance, and database jobs into different scheduling windows.
- Keep
sysstathistory so you can compare CPU, load, and storage activity from before the alert instead of relying on the current snapshot. - Alert on sustained CPU busy time, I/O wait, storage latency, and run-queue growth rather than on load average alone.
- Apply service-level CPU or I/O controls to repeat offenders only after measuring their normal resource needs.
- Investigate long uptimes and pending kernel updates during a planned maintenance window, not during an active performance incident.
sar -u 1 5
sar -q 1 5
sar -d 1 5
Save representative healthy and busy samples. Your own baseline is more useful than a universal threshold.