Your VPS was fine last week. Now MySQL restarts itself at random, SSH sessions freeze for a few seconds before responding, and every so often a background job just... vanishes, no error in the application log at all. If you've run free -h and watched available memory sit near zero right before things go sideways, you're not imagining it — your server is running out of RAM, and Linux's out-of-memory killer is quietly picking off processes to survive.

This is one of the more confusing VPS problems because the symptoms rarely point straight at memory. You get a MySQL crash, a nginx-primary-script-unknown-file-not-found-on-a-vps" class="auto-link">PHP-FPM worker that disappears, or an SSH connection that hangs. Here's how to confirm it's an OOM event, fix it properly, and stop it from happening again.

Symptom: Processes Die Without a Clear Error

The pattern that gives it away is inconsistency. A cron job that runs a big import works fine most nights, then fails silently on the one night traffic was higher. MySQL's error log shows it simply stopped, with no crash trace. PHP-FPM's log shows a worker pool that had to respawn. None of these, on their own, scream "memory," which is why people spend hours chasing the wrong lead — checking disk space, checking CPU graphs, restarting services and moving on until it happens again.

The confirmation is in the kernel log, not the application log. Run:

dmesg -T | grep -i "out of memory"
dmesg -T | grep -i "killed process"

If you see lines like Out of memory: Killed process 4821 (mysqld), that's your answer. The kernel decided it needed RAM back immediately and picked the process using the most of it — usually MySQL, PHP-FPM, or occasionally Apache. You can also check the system log directly:

grep -i "oom" /var/log/syslog
journalctl -k | grep -i "oom-kill"

On CentOS/AlmaLinux, swap /var/log/syslog for /var/log/messages.

Cause: More Demand Than RAM, With No Swap Buffer

The out-of-memory killer only steps in when there's truly nowhere left to put data — no free RAM and no swap space to fall back on. On a lot of budget and mid-tier VPS plans, swap isn't configured by default, so the moment memory usage spikes even briefly, the kernel has no cushion and starts killing processes immediately rather than slowing down gracefully.

The usual triggers we see on hosting tickets:

  • MySQL/MariaDB tuned for a bigger server. A default or copy-pasted my.cnf with innodb_buffer_pool_size set to 2GB on a 2GB RAM droplet leaves nothing for the OS, PHP, or anything else.
  • PHP-FPM with too many workers. If pm.max_children is set high and each worker uses 40-80MB, a traffic spike can multiply memory use fast. Ten workers at 60MB each is 600MB just for PHP.
  • A one-off spike. A large WP-CLI import, a backup script running tar and gzip on a big directory, or a report-generation job that loads too much into memory at once.
  • Multiple services stacked on a small VPS. Running Nginx, PHP-FPM, MySQL, Redis, and a Node app all on a 1-2GB VPS with no swap is asking for trouble the first time two of them peak together.

Check current memory pressure with:

free -h
vmstat 1 5

If "available" is consistently low and swap shows 0 total, you've found the gap.

Fix: Add Swap, Then Right-Size What's Using RAM

Swap won't make a genuinely undersized VPS fast, but it gives the kernel breathing room instead of reaching straight for the OOM killer during short spikes. Add a swap file if you don't already have one:

fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab

Confirm it's active with free -h — you should see the swap line populated. A reasonable starting size is roughly equal to your RAM on plans up to 4GB; beyond that, 2-4GB of swap is usually plenty since you don't want to lean on it heavily anyway (it's disk-backed and much slower than RAM).

Set swappiness lower so the kernel prefers RAM and only spills to swap when it has to, rather than swapping proactively and slowing everything down:

sysctl vm.swappiness=10
echo 'vm.swappiness=10' >> /etc/sysctl.conf

Swap buys you time; it doesn't fix a misconfigured service. Next, right-size the biggest memory consumers:

MySQL/MariaDB — check /etc/mysql/my.cnf (or /etc/my.cnf) and bring innodb_buffer_pool_size down to something sane for your RAM, generally 40-60% of total RAM on a dedicated DB box, much lower if MySQL shares the VPS with everything else. On a 2GB VPS running WordPress, 256-512MB is often more realistic than a copy-pasted 1GB+ value.

innodb_buffer_pool_size = 256M

PHP-FPM — check actual average memory per worker, then set pm.max_children to fit your RAM rather than a guess. In your pool config (often /etc/php/8.x/fpm/pool.d/www.conf):

pm = dynamic
pm.max_children = 8
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 4

A rough formula: (available RAM for PHP) ÷ (average worker memory) = max_children. If PHP averages 60MB per worker and you can dedicate 600MB to it, that's 10 workers, not 40.

Restart both services after changes and re-check free -h under normal load:

systemctl restart mysql
systemctl restart php8.1-fpm

If a specific script or cron job is the trigger — a backup, an import, a report — consider running it with a memory cap or during low-traffic hours, or process the work in smaller batches instead of loading everything at once.

Prevention: Watch It Before It Kills Something

Once swap and service limits are sorted, set up simple ongoing visibility so this doesn't sneak up again:

  • Add a quick memory check to your monitoring (even a basic cron script that emails you if free -h shows under 10% available is better than nothing).
  • Review dmesg for OOM events after any major traffic spike, not just after an outage.
  • If you're consistently near the ceiling even after tuning, that's a sign the VPS plan itself is undersized for what's running on it — upgrading RAM is often cheaper than the engineering time spent fighting it every month.
  • Avoid running memory-heavy one-off tasks (large imports, full-site backups, image processing) during peak traffic hours if they compete with live requests for the same pool.

Table for quick reference on what typically eats RAM and where to look first:

ServiceCommon Memory HogWhere to Check
MySQL/MariaDBinnodb_buffer_pool_size too highmy.cnf
PHP-FPMToo many max_childrenpool.d/www.conf
Backup scriptstar/gzip on large directoriescron job command
Node/custom appsMemory leaks over uptimeprocess manager (pm2, systemd)

Frequently Asked Questions

How do I know if a crash was actually caused by low memory?

Check dmesg -T | grep -i "killed process". If the timestamp lines up with when the service went down, the kernel's OOM killer is confirmed as the cause. If nothing shows there, look elsewhere — a segfault, a disk issue, or an application-level crash.

Will adding swap slow my site down?

Only if the server is relying on it heavily and constantly. A small swap file with low swappiness acts as a safety net for brief spikes, not a replacement for RAM. If free -h shows swap usage climbing and staying high under normal traffic, that's a sign you need more RAM, not more swap.

What's a safe swap size for a 2GB RAM VPS?

2GB is a reasonable starting point — roughly matching total RAM on smaller plans. Going much larger rarely helps since heavy, sustained swap use means the underlying RAM shortage needs fixing directly.

Can I just restart MySQL every time it gets killed instead of tuning it?

You can, but it'll keep happening under the same conditions, and each restart means a few seconds (or longer) of downtime plus a scramble to notice it happened. Tuning innodb_buffer_pool_size to match available RAM fixes the root cause instead of the symptom.

Does upgrading to a bigger VPS plan fix this permanently?

It removes the immediate pressure, but it's worth pairing with proper tuning anyway — an oversized buffer pool or an unbounded PHP-FPM worker count will eventually catch up to a bigger plan too, just later. Right-sizing plus adequate RAM is the combination that actually holds up.