The alert fires at 2:13 a.m. API latency climbs, users report timeouts, and CPU looks high enough to blame.
Storage queues or a database query may reveal the real bottleneck. Rebooting or resizing too early can cause needless downtime and cost.
A slow server troubleshooting checklist separates host-wide slowness from app, database, network, or storage faults. It then compares live readings with a known baseline.
Classify the slowdown before touching the host
Classify the affected layer and immediately stop testing the wrong thing.
Confirm the scope from two locations
Run one request from the server and one from an external US probe. This shows whether the delay starts inside the host or along the public path.
On Linux, run curl -s -o /dev/null -w 'dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}/n' https://example.com/.
On Windows Server, run Measure-Command { Invoke-WebRequest https://example.com -UseBasicParsing } in PowerShell. Record both results with their UTC time.
Two tests from different locations prevent a local network problem from looking like a server fault.
Record the incident before evidence changes
Write down the UTC start time, hostname, public IP, cloud region, and instance ID. Add affected URLs and the last known good time.
List deployments, package updates, backup windows, cron jobs, traffic campaigns, certificate renewals, and config edits. Check the prior 24 hours first.
The most common error here is restarting services before saving this timeline. A restart can erase the only useful clue.
Save the first readings before making changes.
Then inspect the infrastructure below the operating system. Virtual machines can look slow even when the guest itself seems healthy.
Check sustained CPU ready time, high %steal, instance health alerts, and noisy-neighbor patterns. These signs point to shared-host contention.
On Linux, inspect dmesg -T for filesystem, controller, or NIC resets. Run ethtool -S eth0 to check interface errors and drops.
On Windows Server, review System event logs and adapter error counters. Compare nearby instances in the same region when guest readings look normal.
⚠️ Do not blame the application when several same-region instances show latency and normal guest resource use.
Run the first 5, 15, and 30 minutes
Capture proof in five minutes, isolate the bottleneck by 15 minutes, and make only a verified change by 30 minutes.
First five minutes: confirm impact
Check Linux load and active work with uptime. Then run ps aux --sort=-%cpu | head -15 and ps aux --sort=-%mem | head -15.
Run iostat -xz 1 5 when the sysstat package is installed. It takes about five seconds and captures disk delay during the incident.
On Windows, run Get-Process | Sort-Object CPU -Descending | Select-Object -First 15. Open Resource Monitor with resmon for per-process disk and network activity.
A fast check is enough to preserve evidence. A correct check needs five samples, not one.
Minutes five to fifteen: isolate
Check memory pressure before treating high memory use as a fault. Linux uses spare RAM as filesystem cache, and that is normal.
Run free -m, vmstat 1 10, and dmesg -T | grep -Ei 'out of memory|killed process'. Look for growing swap, major paging, or OOM events.
A common case is a cache-heavy host with little free RAM. It is healthy until swap grows or the kernel kills processes.
High used memory alone does not prove memory pressure.
Minutes fifteen to thirty: test one safe fix
Roll back a release only when traces, web logs, error rates, or process growth link it to the release. Do not roll back because timing merely looks suspicious.
For MySQL, run SHOW FULL PROCESSLIST; and check the slow-query log. For PostgreSQL, run SELECT pid, state, wait_event_type, query FROM pg_stat_activity WHERE state <> 'idle';.
Apply one safe change, then retest the same endpoint. Give the host between 10 and 30 minutes under normal traffic.
⚠️ Do not change instance size and database settings together. You will not know which change fixed the delay.
Compare live readings with your baseline
Treat a metric as suspicious when it stays outside its normal pattern under comparable traffic, not simply when it crosses one fixed number. Use the same hour, weekday, region, release version, and request volume for comparison.
Use practical investigation thresholds
| Signal | Investigate when | Check next |
|---|
| CPU use | Above 80% to 90% for 10 minutes | Top processes, request rate, steal time |
| I/O wait | Above 10% to 15% for several samples | Disk latency, queue, backups, IOPS cap |
| Disk latency | Above 20 ms for usual web requests | Volume type, writes, database flushes |
| Memory | Swap grows or OOM events appear | Largest process, leak, worker limits |
| Packet loss | Any steady loss, even 1% | MTR path, NIC errors, regional route |
| Connections | Near the app or database limit | Pool queue, slow clients, leaked sessions |
A reading matters most when it differs from normal traffic at the same time. One fixed threshold cannot diagnose every workload.
Check virtualization and cloud limits
Check cloud controls as well. AWS burstable instances can lose CPU credits.
Azure and Google Cloud set machine and disk limits. DigitalOcean, Vultr, and Linode plans may set fixed bandwidth or volume performance limits.
A CPU graph alone does not diagnose slowness. Moderate CPU with high I/O wait points toward storage. High CPU with low I/O wait points toward compute work, traffic, or inefficient code.
Add network use and interface health to I/O wait checks. Packet loss alone often appears too late.
Investigate when interface use nears the instance, NIC, or egress limit. Also investigate rising TCP retransmits or receive and transmit errors.
On Linux, compare sar -n DEV 1 10, ss -s, and ethtool -S eth0 with baseline readings. Windows Server can sample Bytes Total/sec, Output Queue Length, Packets Received Errors, and TCPv4/Segments Retransmitted/sec.
Review the storage queue alongside these readings. A busy backup or replication stream can strain both disk and network.
⚠️ Do not treat a full network link as a routing issue until you rule out backups, replication, and large outbound transfers.
Map each symptom to a verified action
Match the visible symptom to one abnormal signal, verify the likely cause, and then apply the smallest correction that can work.
Follow the symptom-to-action tree
| Symptom | Verify | First action |
|---|
| All services slow, high CPU, low I/O wait | Top process, traffic, `%steal` | Stop runaway work, add cache, then assess vCPU |
| All services slow, high I/O wait | `iostat -xz 1 5`, queue, backup timing | Pause matching I/O job or raise proven volume limit |
| Website slow, host healthy | APM trace, PHP-FPM queue, query log | Fix endpoint, worker pool, cache, or query |
| One region is slow | `mtr -rw host` or `pathping host` | Escalate routing or adjust CDN region |
For a fast Linux disk test, run iostat -xz 1 5. Check await, %util, and queue depth.
For Windows Server, check Avg. Disk sec/Transfer, Current Disk Queue Length, and Disk Transfers/sec. Use Performance Monitor for these counters.
Escalate with evidence providers can use
Open a provider case for sustained host contention, volume latency beyond plan behavior, or outside packet loss. Also escalate instance health warnings or a regional event.
Include UTC timestamps, instance ID, region, path details, graphs, disk readings, and MTR output. Include pathping output for Windows hosts.
Provider support can act faster with time-bound evidence. A vague report of "slow server" rarely identifies the failed layer.
For slow database queries, separate active work from blocking and exhausted connections. Restarting a database can hide the blocking session.
In MySQL, run SHOW FULL PROCESSLIST; first. Then inspect the slow-query log and lock waits.
In PostgreSQL, pair pg_stat_activity with lock and wait-event checks. wait_event_type = 'Lock' means you should find the blocking session.
If I/O waits recur, check storage as well. Check pool use, idle-in-transaction sessions, query duration, rows examined, and an EXPLAIN plan.
Record the query fingerprint, affected endpoint, and before-and-after timings. Test the fix under comparable traffic.
⚠️ Do not add an index or raise worker limits before checking locks and query plans. Either change can worsen write load.
Document the fix and avoid repeat incidents
Document the incident while the data is still available, then alert on the leading signal that appeared before users noticed slowness.
Copy this incident record
text
UTC start and end:
Host / instance ID / region:
Affected scope: host | app | database | storage | network
User impact: URLs, regions, HTTP status codes, TTFB:
Recent changes in prior 24 hours:
Baseline compared: date, traffic level, release version:
CPU / I/O wait / memory swap / disk latency:
Network use / packet loss / connection count:
Top process or slow query:
Verification performed:
Change made and exact time:
Result after 10 and 30 minutes:
Escalation ticket and owner:
Follow-up alert or capacity change:
The incident record turns a one-time fix into a repeatable response. Fill it in during the incident, not the next day.
Set an alert on the first abnormal sign. That may be I/O wait, queue depth, retransmits, or slow query time.
A useful alert fires before users see timeouts.
⚠️ Do not mark the incident resolved after one fast request. Check the same endpoint and resource signals for 10 to 30 minutes under normal traffic.
This process does not fit a problem seen by one user only. Check that user's browser and local network first. It also does not replace a security review when unknown processes or odd traffic appear. Confirmed DNS, CDN, SaaS, or regional outages need their own provider incident process.
Use this record in your next provider ticket or capacity review. It gives you proof before spending money on a larger plan.
Your questions answered
Why is my server slow when CPU is low?
Low CPU with I/O wait above 10% to 15% often means storage delays work. Check disk latency, queue depth, backups, database writes, and cloud volume limits before adding vCPU.
Is 80% CPU usage too high for a server?
No, 80% CPU can be normal during expected load. Investigate when it stays near 80% to 90% for 10 minutes. Compare it with normal traffic at the same time.
How do I check I/O wait on Linux?
Run iostat -xz 1 5 and inspect %iowait in top or vmstat 1 5. I/O wait above 10% to 15% needs storage checks when await also rises.
How do I troubleshoot a slow Windows server?
Check CPU, Available MBytes, disk transfer latency, and established TCP connections. Use PowerShell counters or Performance Monitor. Disk latency above about 20 ms and rising Pages/sec need investigation.
Should I reboot a slow VPS?
Reboot only after saving processes, logs, connections, and resource readings. Skip that delay only when availability or security needs immediate action. A reboot can hide credit loss, memory leaks, or runaway tasks.
Contact the provider when readings show host contention, volume latency, route loss, or instance health events. Include UTC timestamps, region, instance ID, graphs, MTR results, and changes from the prior 24 hours.
Keep proof before changing capacity
Fix the constrained layer, not the metric that looks most alarming. Preserve evidence, compare it with a matching baseline, and test one correction at a time.
A larger server cannot fix a blocked query or capped volume. Evidence tells you where to spend time and money.
Learn more
Here are some additional resources on this subject: