Most “my VPS is slow” reports end with the same root cause: not a weak CPU or saturated network, but a web stack configured with defaults that assume more memory than the box has. Nginx, PHP-FPM, and Redis each have a handful of settings that decide whether a 2 GB VPS serves a busy site comfortably or swaps itself into a crawl. Before sizing anything, sanity-check the host itself against the plans on our VPS comparison table — tuning cannot manufacture RAM that was never allocated.
Measure First: Where Is the Time Going?
Capture a baseline before changing anything, so you can prove the effect of each tweak:
# Total request time, plus a breakdown
curl -o /dev/null -s -w 'connect: %{time_connect}s\n'
-w 'ttfb: %{time_starttransfer}s\n'
-w 'total: %{time_total}s\n' https://your-vps.example/
# Where memory actually goes
free -h
ps aux --sort=-%mem | head -12
If TTFB is high but the page itself is small, the delay is almost always PHP-FPM waiting for a free worker, or PHP waiting on a slow query — not Nginx. If TTFB is fine but total time is high, look at assets and caching next. Tune the layer the numbers point at.
PHP-FPM: Size the Pool with Memory Math, Not Guesses
The single most impactful PHP-FPM setting is pm.max_children. The default pm = dynamic with max_children = 50 assumes far more memory than a small VPS has. Do the arithmetic:
# Average RSS of one PHP-FPM worker under load ps -o rss= -C php-fpm8.2 | awk '{s+=$1; n++} END {printf "avg RSS: %.0f MB (%d workers)\n", s/n/1024, n}'Suppose workers average 80 MB and your box has 2 GB total with Nginx, MySQL, and Redis claiming roughly 800 MB. That leaves about 1.2 GB for PHP, so
pm.max_children = 15(15 × 80 MB = 1.2 GB) is the honest ceiling — not 50. Setpm = ondemandfor low-traffic sites so idle workers are freed instead of parked, and enable the slow log to find the scripts that hold workers hostage:pm = ondemand pm.max_children = 15 pm.process_idle_timeout = 10s slowlog = /var/log/php-fpm-slow.log request_slowlog_timeout = 5sWatch
tail -f /var/log/php-fpm-slow.logfor a week. If the same function appears repeatedly, fix the query or cache its result — raisingmax_childrento paper over a slow script just moves the problem to the OOM killer.Nginx: Small Changes, Measurable Gains
worker_processes auto;— one worker per vCPU; more workers than cores only add context-switch overhead on a shared host.worker_connections 1024;— 1024 connections per worker is plenty for a small VPS; raising it without raisingworker_rlimit_nofiledoes nothing.keepalive 65;andkeepalive_requests 100;— reuse upstream connections instead of renegotiating TLS per request.gzip on; gzip_comp_level 5; gzip_types text/css application/javascript application/json;— compressing CSS/JS/JSON is the cheapest bandwidth win available.
If PHP-FPM is on the same box, make sure fastcgi_pass uses a Unix socket rather than TCP (unix:/run/php/php8.2-fpm.sock), which avoids loopback overhead and one more thing for a firewall to misconfigure.
Redis: Cache the Right Layer
Redis earns its 50–100 MB of RAM when it caches the expensive layer — PHP object cache (WordPress, Magento, Laravel), session storage, or rate-limit counters. Pointless when it only caches what the page cache already handles. Verify it is actually being used:
redis-cli info stats | grep -E 'keyspace_hits|keyspace_misses' redis-cli info memory | grep used_memory_humanA hit ratio below 80% means either the cache is being flushed constantly or the application is not really using it. Cap memory with
maxmemory 256mbandmaxmemory-policy allkeys-lruso Redis never becomes the thing that OOMs your PHP-FPM workers.Re-Measure and Lock It In
Run the same
curltiming from the beginning. On a typical WordPress site the combination of a correctly sized FPM pool, Nginx micro-optimizations, and a warm Redis object cache cuts TTFB from 400–600 ms to 80–150 ms. If you also enable a full-page cache for anonymous visitors, total time drops into the tens of milliseconds — at which point the bottleneck moves to your connection, not the server.The pattern is the same on every VPS: size workers to real memory, cache the layer that is actually expensive, and re-measure after every change. If your current plan cannot fit Nginx, PHP-FPM, and a database without swap churn, the memory and storage comparison on our site helps you find a tier that can, and the provider ranking lists hosts where these settings translate into real throughput rather than burstable marketing numbers.


Leave a Reply
You must be logged in to post a comment.