High CPU Usage
A runbook for diagnosing sustained high CPU usage on a Linux server — from identifying the offending process down to the exact thread and code path.
Overview
CPU pegged at 100% (or a load average far above core count) usually has one of two shapes: a single runaway process, or many processes collectively saturating the box. The investigation path differs for each.
The Problem
A server is sluggish, alerts show CPU at 90-100%, and users report slow responses or timeouts.
Investigation
uptime # load average vs. nproc — is this actually CPU-bound?
nproc # number of cores, for context
top -o %CPU # live view, sorted by CPU — usually shows the culprit immediately
ps aux --sort=-%cpu | head -10 # top 10 CPU consumers, snapshot
mpstat -P ALL 1 5 # per-core breakdown — one core pegged vs. all cores busy tells different stories
Once you’ve found the offending PID:
ps -p <PID> -o %cpu,%mem,etime,cmd # how long has it been running? memory usage too?
cat /proc/<PID>/status # detailed process state
top -H -p <PID> # per-THREAD CPU usage within that one process
For a deeper look at what the process is actually doing:
strace -c -p <PID> # summarize which syscalls it's making, and how often
perf top -p <PID> # (if perf is installed) sampling profiler — shows hot functions
Root Cause Possibilities
- A single runaway process — infinite loop, retry storm, or a stuck request handler that never returns.
- Too many processes for the box — legitimate load has outgrown the instance size.
- A cron job overlap — the same scheduled task running concurrently because a previous run never finished (see Cron & Scheduling).
- Garbage collection thrashing (JVM/Node apps) — CPU spent almost entirely on GC instead of real work, often paired with high memory pressure.
- A crypto-mining or compromise scenario — an unfamiliar process consuming CPU that nobody on the team recognizes.
Resolution
# If it's a genuine bug/runaway process — stop it gracefully first
kill -15 <PID> # SIGTERM: let it clean up
kill -9 <PID> # SIGKILL: only if it doesn't respond to SIGTERM
# If it's a resource-sizing issue (legitimate load, undersized instance)
# — scale vertically (bigger instance) or horizontally (more instances/replicas)
# If it's an unfamiliar/suspicious process
ps -p <PID> -o cmd,user,lstart # who started it, and when
lsof -p <PID> # what files/network connections it has open
# isolate and investigate before killing — this may be a security incident, not a bug
Prevention
- Set resource limits (
cgroups, Kubernetesresources.limits.cpu) so one runaway process can’t starve the whole host. - Add
flockto cron jobs that could overlap (see Cron & Scheduling tutorial) to prevent overlapping runs from compounding CPU load. - Alert on load average relative to core count (
load / nproc), not an absolute number — the same load average means very different things on a 2-core vs. 32-core box. - Baseline normal CPU usage per service so an unfamiliar spike is caught quickly, not normalized away.
Summary
High CPU is diagnosed top-down: top/ps --sort=-%cpu to find the process, then top -H -p or strace -c -p to see what it’s actually doing inside. The fix depends entirely on the root cause — kill a runaway process, scale a genuinely undersized host, fix an overlapping cron job, or escalate as a possible security incident for an unrecognized process.
Pro Tip
Always check mpstat -P ALL before assuming it’s “one runaway process” — a single core pegged at 100% while others sit idle often points to a single-threaded bottleneck in the application itself, not something kill will meaningfully fix without a code or configuration change.
Add More Questions to This Guide
Know a question that should be here? Share it and help the community!
Open Google Form