Apache Performance Tuning on a VPS: MPM, KeepAlive, and Caching Configs That Matter

Apache has a reputation for being memory-hungry and slow under high concurrency — a reputation that mostly comes from misconfigured default installs. On a VPS with 2–4 GB of RAM, a stock Apache with the prefork MPM and mod_php can exhaust memory at a few hundred concurrent connections. Reconfigured with the event MPM, PHP-FPM, compression, and caching, the same Apache instance can serve thousands of requests per second on the same hardware.

This guide walks through the Apache settings that matter most on a VPS, with exact configs and an ab benchmark to prove the difference. If you are still deciding between web servers for a small instance, the nginx vs Apache comparison on this site covers the trade-offs; and to see which provider’s plans give you enough RAM headroom for a threaded MPM, check our VPS comparison table.

1. Use the Event MPM, Not Prefork

The MPM (Multi-Processing Module) defines how Apache handles connections. prefork spawns one process per connection, each with a full copy of the interpreter — the memory killer on small VPS. The event MPM uses a small number of processes with many threads per process, and handles keep-alive connections in a dedicated thread pool. The result is roughly an order of magnitude fewer processes for the same traffic.

# Debian/Ubuntu
a2dismod mpm_prefork
a2enmod mpm_event
systemctl restart apache2

# Verify
apache2ctl -M | grep mpm

One condition: event requires PHP to run as a separate process (PHP-FPM), not as mod_php. That is step 3 — and it is a requirement, not a suggestion.

2. Size MaxRequestWorkers to Your RAM

The single most common Apache failure on a VPS is running out of memory because MaxRequestWorkers is far too high. Each worker thread costs memory, so compute the ceiling from your available RAM:

# Measure a real worker's memory footprint:
ps -o rss,cmd -C apache2 | awk 'NR>1 {sum+=$1} END {print "avg KB/worker:", sum/(NR-1)}'

# On a 2 GB VPS with ~1.6 GB usable and ~30 MB per worker:
# MaxRequestWorkers ~ (1.6GB * 1024) / 30MB ≈ 50

    StartServers            3
    MinSpareThreads         10
    MaxSpareThreads         40
    ThreadsPerChild         25
    MaxRequestWorkers       50
    MaxConnectionsPerChild  10000

Start conservative (40–60 workers on a 2 GB VPS) and raise it while watching free -h under load. An OOM-killed Apache is far worse than a few queued requests.

3. Serve PHP Through PHP-FPM

Running PHP inside Apache ties every worker to a PHP interpreter and forces the prefork model. Moving PHP to FPM lets Apache stay threaded and gives you per-pool memory limits, process control, and better opcode caching:

apt install php-fpm
a2dismod php8.3
a2enmod proxy_fcgi setenvif
a2enconf php8.3-fpm
systemctl restart apache2 php8.3-fpm

# Per-pool memory cap in /etc/php/8.3/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 8
pm.max_requests = 500

With pm.max_requests = 500, PHP-FPM recycles workers periodically, which prevents memory leaks from accumulating — a common cause of slow degradation on long-running VPS.

4. KeepAlive: On, but Short

KeepAlive lets a client reuse the TCP connection for multiple requests — essential for pages with many assets. The mistake is leaving KeepAliveTimeout at the default 5–15 seconds, which pins worker threads to idle connections. Two to three seconds is plenty for HTTP/1.1:

KeepAlive On
KeepAliveTimeout 2
MaxKeepAliveRequests 100

5. Compression and Caching

Compression is the cheapest bandwidth win on a VPS. Enable mod_deflate (or mod_brotli if available) for text assets, and use mod_expires so browsers cache static files instead of re-requesting them:

a2enmod deflate expires headers

# /etc/apache2/conf-available/compression.conf
AddOutputFilterByType DEFLATE text/html text/css application/javascript application/json
ExpiresActive On
ExpiresByType image/png "access plus 1 month"
ExpiresByType text/css "access plus 1 week"

For dynamic content, add a reverse cache in front (Varnish) or use mod_cache with CacheEnable disk for cacheable responses. Even a 60-second cache on expensive pages can cut Apache’s CPU load by half on a traffic spike.

6. Trim Modules You Do Not Use

Every loaded module costs memory per worker and CPU per request. List what is active and disable anything unnecessary:

apache2ctl -M
# Common candidates for removal on a lean VPS:
a2dismod autoindex status info userdir
systemctl restart apache2

7. Benchmark Before and After

Use ApacheBench against a small PHP page, and compare requests/sec and the memory footprint before and after each change:

ab -n 5000 -c 50 http://127.0.0.1/index.php
free -m

On a 2 vCPU / 2 GB VPS, the typical before/after with the full recipe above is 3–5x more requests/sec and 60% lower memory usage. The event MPM plus PHP-FPM is the bulk of the win; compression and caching are the multiplier.

Conclusion

Apache is not the bottleneck on a VPS — a default configuration is. Switch to the event MPM, move PHP to FPM, size workers to RAM, and add compression and caching. Benchmark after every change, and keep an eye on free -h under load. If your plan is too small to run a threaded MPM comfortably, consider a host with more RAM headroom — see the full specs across providers to find one that fits. For a budget-friendly KVM VPS with enough memory for a tuned Apache stack, InterServer is a popular choice among readers of this site.

Leave a Reply