CPU Looks Fine but the VDS Is Slow: A Linux Incident Runbook
A user reports that an application is slow or intermittently unreachable. The monitoring dashboard shows 20% CPU, memory still available, and no obvious outage. It is tempting to answer, “the server looks healthy.” That answer is usually premature. A single CPU graph cannot tell you whether a Linux guest is waiting on storage, reclaiming memory, losing packets, stalling inside a cgroup,…
A user reports an application running slowly or intermittently becoming unreachable. Initial monitoring shows 20% CPU usage, available memory, and no obvious problems. It's tempting to assume the server is healthy, but this isn't enough. To determine if a Linux guest is experiencing issues, we need to identify where the request is spending time. This runbook provides a repeatable method to diagnose the problem on a Linux virtual server, separating guest-level faults from upstream issues.
First, freeze the incident boundary by documenting the exact start and end times in UTC, the affected hostname, IP, port, and protocol, whether all clients are affected, and the specific symptom (timeout, connection reset, slow TTFB, packet loss, or high application latency. Capture a small identity bundle including the date, hostname, kernel version, uptime, and who login information.
Next, examine CPU usage as a scheduler rather than a percentage. Use vmstat, mpstat, and pidstat commands to gather data on runnable tasks, CPU usage, context switches, and migrations. High aggregate CPU doesn't necessarily mean saturation, as a single-threaded worker can max one vCPU while the average across multiple vCPUs remains near 12.5%. Analyze per-CPU output to spot patterns.
Examine memory pressure instead of just available memory. Use free, vmstat, /proc/pressure/memory, ps, and journalctl commands to look for memory reclaim activity, OOM messages, and memory-related cgroup issues. Host memory may appear adequate while a service hits its cgroup limit.
Check storage latency, not just disk usage. Verify capacity and inode availability, then sample the block layer using iostat, pidstat, /proc/pressure/io, and journalctl. Look for average I/O request time, queue size, and I/O PSI. A large await value or sustained queues indicate the device cannot keep up with the workload.
Finally, inspect sockets and the network stack. Use ss, ip, sar commands to analyze retransmissions, drops, socket status, and TCP RTT. If the interface counters appear normal, examine each layer of the request path, including the resolver, firewall, reverse proxy, application listener, and upstream dependencies. Compare authoritative and recursive DNS answers using dig for DNS-specific issues.
By following this runbook, you can identify the root cause of a Linux guest's performance issues, whether it's storage, memory, or network-related problems.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.