High server load
If your VPS starts responding slowly, sites hang, and SSH barely reacts to input, high load is the likely cause. This article covers how to measure the load and figure out whether it's actually high for your particular server, how to find the specific process or cause behind it (CPU, memory, or disk), and which fix applies: throttling or killing a process, adding swap, tuning configuration — or moving to a plan with more resources.
What you'll need
- SSH access as
rootor a user withsudo— all the commands below are run in the terminal. - Nice to have (not required) — access to the VMmanager 6 panel: useful for viewing CPU/RAM/disk graphs over a longer period, and for increasing server resources if needed. If you haven't logged in yet, see "How to log in to VMmanager 6".
Step 1. Check load average and compare it to your CPU count
The fastest way to see how loaded the server is — the uptime command:
uptimeExample output:
14:32:07 up 21 days, 3:12, 2 users, load average: 0.52, 0.58, 0.61The three numbers after load average are the average length of the queue of processes that are either ready to run (state R) or waiting on disk I/O (state D), averaged over the last 1, 5, and 15 minutes. This is not a CPU usage percentage — it's the number of processes that, on average, didn't have a free core or disk available to them right at that moment.
On their own, these numbers mean nothing without knowing how many CPU cores the server has:
nprocDivide the load average by the result of nproc to get the average load per core:
- All three numbers are around
1.0per core or lower — the server is keeping up. - The 1-minute figure is noticeably higher than the 5- and 15-minute ones — a short spike (a cron job ran, a one-off burst of requests), usually nothing to worry about.
- The 5-minute and especially the 15-minute figure is consistently above
1.0per core — the load isn't a one-off: processes are genuinely queueing for CPU or disk.
You'll see the same three numbers in the header of top — a separate uptime is only useful for a quick glance without opening the interactive interface:
topBy default, top shows CPU usage as a single combined %Cpu(s) line. Press 1 to switch to a separate line per core — that immediately shows whether the load is spread evenly or is actually pegged on just one core (a classic symptom of a single-threaded bottleneck, where the overall load average looks low across all cores but everything is really stuck on one of them).
Step 2. Find the process that's loading the server
top sorts processes by %CPU by default, so the heaviest process shows up right at the top:
topFor a non-interactive snapshot — handy for copying or saving to a log:
top -bn1 | head -20If you'd rather have a friendlier interface, install htop:
Ubuntu / Debian:
sudo apt install htopAlmaLinux / Rocky (htop isn't in the default repositories — enable EPEL first):
sudo dnf install -y epel-release
sudo dnf install -y htophtophtop also sorts by %CPU by default, and shows a separate load meter for every core right at the top of the screen. The F6 key opens a menu to pick a different sort field (memory, for example), and F9 sends a signal to a process (including killing it) right from the list, without switching to another terminal.
Without an interactive UI, to sort by CPU or by memory separately:
ps aux --sort=-%cpu | head -10ps aux --sort=-%mem | head -10Of all the output columns, the ones that matter most are PID, %CPU, %MEM, and COMMAND. The STAT column uses the same state letters as the S column in top: R — running, D — waiting on disk I/O (if you see D next to a process near the top of the list, the likely cause is disk, not CPU — see Step 5), S — sleeping, Z — zombie.
The shape of the list itself tells you which way to go next:
- One process clearly stands out in
%CPUor%MEMcompared to the rest — a runaway process, see Step 6, "One process has gotten out of control." - The load is spread across dozens of same-type processes (lots of
php-fpm,node, or your web server's/app's own worker processes) — that's not one broken process, it's real load from traffic or an app configuration that isn't tuned for this server, see Step 6, "Legitimate load or poorly tuned software."
Step 3. Check memory and swap
Even if CPU looks fine, a shortage of RAM slows down the entire server — through heavy swapping.
free -hExample output:
total used free shared buff/cache available
Mem: 3.8Gi 2.9Gi 120Mi 45Mi 900Mi 650Mi
Swap: 2.0Gi 1.6Gi 412MiLook at the available column in the Mem row — it isn't literally "free" memory, it's an estimate of how much can actually be handed to new processes without swapping (accounting for page cache the kernel can reclaim). If available is low, and used in the Swap row is high and climbing, that's memory pressure.
Check whether swap is even configured on the server at all:
swapon --showEmpty output means there's no swap at all — in that case, once RAM runs out, the kernel has only one option left, covered in Step 4.
Current swap activity is shown by vmstat:
vmstat 1 5The si and so columns show kilobytes per second read from and written to swap. Consistently non-zero values mean the server is actively swapping right now: what should be memory access is actually going through disk, which is orders of magnitude slower than RAM — that's exactly why the whole system feels sluggish, even if individual processes' %CPU doesn't look particularly high.
Step 4. Check whether the OOM killer has fired
When RAM and swap run out completely, the Linux kernel doesn't just slow down — it forcibly terminates one of the processes to keep the system alive at all (the OOM killer, Out-Of-Memory killer). This explains sudden crashes of exactly the "heaviest" process (often a database or another memory-hungry service) with no error at all in that application's own logs.
sudo dmesg -T | grep -Ei 'out of memory|killed process'Example line from the output:
[Thu Jul 9 11:47:02 2026] Out of memory: Killed process 18342 (mysqld) total-vm:2145392kB, anon-rss:1583020kB, file-rss:0kB, shmem-rss:0kB, UID:112 pgtables:3456kB oom_score_adj:0dmesg's ring buffer is cleared on every reboot. To search for these events over a longer period, use the system journal — the same kernel messages, but kept across reboots:
sudo journalctl -k --no-pager | grep -Ei 'out of memory|killed process'If you find entries like this, it confirms that at that moment memory — not CPU or disk — was the bottleneck. Move on to Step 6, "Not enough RAM," or, if this keeps happening, straight to "Genuinely not enough resources — upgrade the plan."
Step 5. Check disk I/O
High load sometimes comes down not to CPU or memory, but to the disk itself — processes sit in the queue in state D, waiting for the disk to service a read or write.
The iostat utility ships as part of the sysstat package — it's in the default repositories on every target distro, no EPEL needed:
Ubuntu / Debian:
sudo apt install sysstatAlmaLinux / Rocky:
sudo dnf install -y sysstatRun it with extended statistics:
iostat -xz 1 3-x — extended statistics (including %util and await), -z — omit devices with no activity during the interval, 1 3 — three reports at a 1-second interval. The first iostat report always shows averages since the server booted — pay attention to the second and later reports, which reflect the actual current interval.
Example (trimmed — not all columns of the real output are shown):
Device r/s w/s %util await
vda 12.40 58.20 97.50 42.30%util— the percentage of time during which the device had I/O requests outstanding. For a single disk, a value close to 100% almost always means the disk is the bottleneck. For a RAID array made up of several disks, this figure is less clear-cut — the device can serve several requests in parallel.await— the average time to service a request, in milliseconds, including both time spent queued and time actually being serviced. On SSD/NVMe storage, a few milliseconds is normal; a sustained tens-to-hundreds of milliseconds is a clear sign the disk can't keep up.
Step 6. Common causes and fixes
One process has gotten out of control
If Step 2 turned up a specific offending process, try stopping it cleanly first:
kill -TERM 18342If it's still alive a few seconds later, force it:
kill -9 18342If it's a service managed by systemd (a web server, a database, and so on), it's better not to kill the process directly but to restart the service itself — that way it comes back up in a clean state:
sudo systemctl restart mysqlThe actual service name may be different — for example, if it's a database, see "MySQL / MariaDB: install and configure" or "Redis: install and configure".
If you'd rather not kill the process and just make it less greedy, lower its CPU priority:
sudo renice -n 15 -p 18342The range runs from -20 (highest priority) to 19 (lowest). The process isn't terminated, but it yields to others when they're competing for CPU.
To hard-cap its share of CPU (say, no more than 50% of one core) without touching priority, use cpulimit:
Ubuntu / Debian:
sudo apt install cpulimitAlmaLinux / Rocky (needs EPEL — no need to add it again if you already did for htop):
sudo dnf install -y epel-release
sudo dnf install -y cpulimitcpulimit -p 18342 -l 50cpulimit runs in real time in the foreground. For the limit to keep working after you close your SSH session, run it in the background: cpulimit -p 18342 -l 50 &.
For a systemd service, it's more reliable to set this kind of limit through systemd itself — it survives the process restarting and doesn't need a separate utility running alongside it:
sudo systemctl set-property mysql.service CPUQuota=50% MemoryMax=1GThis applies immediately, with no service restart, and persists across server reboots.
Not enough RAM
df -h /If space is tight or already gone, deal with "Disk is full" first.
Create a swap file (2G here is an example — size it to your server's memory and free disk space):
sudo fallocate -l 2G /swapfilesudo dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progressLock down the file's permissions, initialize it as swap, and turn it on:
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfileFor the swap file to be mounted automatically after a reboot, add an entry to /etc/fstab:
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstabVerify the result:
sudo swapon --show
free -hYou can also lower vm.swappiness — a kernel parameter (default 60, range 0–200) that controls how eagerly the kernel reaches for swap instead of reclaiming the page cache:
sudo sysctl vm.swappiness=10
echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swappiness.confLegitimate load or poorly tuned software
- The number of workers/processes doesn't match the number of cores — too many workers compete for the same cores, too few don't use all of them. Base it on
nprocand the recommendations for your particular stack: "Python and FastAPI: production deployment", "Deploying Django on a VPS", "Node.js apps with PM2 and Nginx". - The database has no memory limit and competes for RAM with everything else — size its buffers to the server's actual RAM: "MySQL / MariaDB: install and configure", "PostgreSQL: install and configure". For Redis, use the
maxmemorydirective together with an eviction policy inmaxmemory-policy— see "Redis: install and configure". - A backup or another cron job landed at the same time as your peak load — compare the load average spike from Step 1 with your job schedule:
crontab -lsystemctl list-timersIf they line up, move the heavy jobs to a quieter time of day: "Scheduled backups with cron", "Incremental backups with restic", "Backups with rsync".
- Database queries without indexes, N+1 queries, no caching — the most common cause of "traffic didn't really grow, but load did." At this point it's a question of the application's own code and configuration, not the VPS itself.
Genuinely not enough resources — upgrade the plan
If, after stopping or throttling stray processes and optimizing, the server is still consistently maxed out under load that's genuinely necessary, it's probably time to grow along with the project rather than keep fighting the symptoms.
- Check the CPU/RAM/disk graphs for the last few days or weeks on the server's card in VMmanager 6 — if the load is consistently high rather than a one-off spike, that's the signal that your current plan doesn't have enough resources for your project.
📷 [screenshot: CPU/RAM load graph on the VPS card in VMmanager 6]
- Upgrading vCPU, RAM, or disk is done from the my.mhost.ee customer portal (or through mHost support).
If the server looks compromised
Sometimes what looks like "load from traffic" has nothing to do with your application or with legitimate visitors at all — the server may have been compromised and put to work mining cryptocurrency, sending spam, or taking part in DDoS attacks. Signs that this is a compromise, not a configuration issue:
- An unfamiliar name in the process list (Step 2) that you never installed — especially one running from
/tmp,/var/tmp, or/dev/shm. - You kill a process (
kill -9), and a few seconds later it's back — with the same PID or a different one. - Outbound network activity has spiked for no obvious reason:
sudo ss -tnpcrontab -lor the files under/etc/cron.d/contain jobs you definitely didn't add.
What's next
If, after all these steps, the server still periodically bogs down specifically under peak but genuinely necessary load, the issue probably isn't a one-off fault — it's that your current plan needs to grow along with the project (see Step 6 above). Related topics: if load means the site doesn't just slow down but stops loading entirely, see "My website won't load"; if a service itself won't start rather than just eating resources, see "A service won't start"; if this looks more like running out of disk space than CPU/RAM, see "Disk is full".