PHP Performance Tuning on VPS: OPcache, PHP-FPM, and JIT Compiler Configuration
PHP 8.x with proper OPcache configuration delivers 3–10× better performance than PHP 7.x with default settings. OPcache eliminates PHP’s most expensive operation — parsing and compiling PHP scripts on every request — by caching compiled bytecode in shared memory. The JIT compiler (PHP 8.0+) converts hot bytecode to native machine code. PHP-FPM pool tuning determines how many concurrent requests your VPS handles efficiently.
Baseline: Measure Before Optimizing
# Benchmark current performance with Apache Bench
ab -n 500 -c 10 https://yourdomain.com/
# Key metrics to record before and after:
# - Requests per second: X #/sec
# - Mean time per request: X ms
# - 95th percentile response time: X ms
Step 1: Configure OPcache
OPcache is the single highest-impact PHP performance change — it eliminates PHP compilation on every request by caching compiled bytecode in shared memory.
sudo nano /etc/php/8.2/fpm/conf.d/10-opcache.ini
[opcache]
; Enable OPcache
opcache.enable=1
opcache.enable_cli=0 ; Don't cache CLI (Composer, WP-CLI) — not needed
; Shared memory for compiled bytecode
; WordPress with active plugins typically needs 128–256 MB
opcache.memory_consumption=256
; Shared memory for string interning (class/function names)
opcache.interned_strings_buffer=32
; Maximum number of PHP files to cache
; Count your PHP files: find /var/www -name "*.php" | wc -l
; Set to 2× that count to avoid evictions
opcache.max_accelerated_files=20000
; How often to check for file changes (seconds)
; 0 = check every request (development only!)
; 300 = check every 5 minutes (production recommended)
opcache.revalidate_freq=300
; Must be 1 for Laravel, WordPress, and most frameworks
opcache.save_comments=1
; Fast shutdown improves memory release speed
opcache.fast_shutdown=1
; Override file_exists() checks for cached files
opcache.enable_file_override=1
Step 2: Enable JIT Compiler (PHP 8.0+)
JIT converts PHP bytecode to native machine code at runtime. Significant benefit for CPU-intensive code; minimal benefit for I/O-bound apps like typical WordPress installs.
# Add to /etc/php/8.2/fpm/conf.d/10-opcache.ini:
; JIT buffer size (native code cache)
opcache.jit_buffer_size=128M
; JIT mode:
; tracing = Best for web workloads (profiles actual execution paths)
; function = Per-function JIT (good for libraries)
; on = Alias for tracing
opcache.jit=tracing
JIT benefit by workload type:
- High benefit: Image processing (GD, Imagick), cryptography, mathematical computation, data transformation scripts
- Moderate benefit: Laravel/Symfony framework boot, template rendering
- Low benefit: WordPress page rendering (mostly I/O — database queries, file reads)
sudo systemctl restart php8.2-fpm
Step 3: PHP-FPM Pool Tuning
sudo nano /etc/php/8.2/fpm/pool.d/www.conf
Dynamic Mode (Recommended for Most Sites)
[www]
user = www-data
group = www-data
listen = /run/php/php8.2-fpm.sock
listen.owner = www-data
listen.group = www-data
; Dynamic: scales worker count with demand
pm = dynamic
; Worker count formula: (available RAM - 200 MB OS overhead) / avg PHP process size
; Average WordPress PHP process: 40–80 MB
; Example for 2 GB VPS: (2000 - 200) / 60 MB = ~30 max, start conservatively
pm.max_children = 10
; Workers at startup
pm.start_servers = 3
; Minimum idle workers (always ready for traffic)
pm.min_spare_servers = 2
; Maximum idle workers (kill extras when traffic drops)
pm.max_spare_servers = 5
; Restart each worker after N requests (prevents gradual memory leak)
pm.max_requests = 500
; Kill requests taking longer than this (seconds)
request_terminate_timeout = 120
; Log slow requests (helps find slow queries or plugins)
slowlog = /var/log/php8.2-fpm-slow.log
request_slowlog_timeout = 5s
; Enable status endpoint (for monitoring)
pm.status_path = /fpm-status
OnDemand Mode (Low-Traffic or Shared VPS)
; Start workers only when requests arrive — saves RAM when idle
pm = ondemand
pm.max_children = 10
pm.process_idle_timeout = 10s ; Kill idle workers after 10 seconds
pm.max_requests = 200
sudo systemctl restart php8.2-fpm
Step 4: Per-Site PHP-FPM Pools
For multiple sites on one VPS, separate pools prevent one site’s traffic from starving others:
sudo cp /etc/php/8.2/fpm/pool.d/www.conf /etc/php/8.2/fpm/pool.d/site1.conf
sudo nano /etc/php/8.2/fpm/pool.d/site1.conf
[site1]
user = site1_user
group = site1_user
listen = /run/php/php8.2-site1.sock ; Unique socket per site
pm = dynamic
pm.max_children = 5
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 200
sudo systemctl restart php8.2-fpm
Reference the site-specific socket in Nginx:
fastcgi_pass unix:/run/php/php8.2-site1.sock;
Step 5: Check OPcache Effectiveness
# Create a one-time status check script
sudo -u www-data php -r "
\$s = opcache_get_status();
echo 'Hit rate: ' . round(\$s['opcache_statistics']['opcache_hit_rate'], 2) . \"%\n\";
echo 'Cached files: ' . \$s['opcache_statistics']['num_cached_scripts'] . \"\n\";
echo 'Memory used: ' . round(\$s['memory_usage']['used_memory'] / 1024 / 1024, 1) . \" MB\n\";
echo 'Memory free: ' . round(\$s['memory_usage']['free_memory'] / 1024 / 1024, 1) . \" MB\n\";
"
Healthy OPcache targets:
- Hit rate > 95%: If below 90%, increase
max_accelerated_filesormemory_consumption - Memory used < 80% of limit: If above 90%, increase
memory_consumption - Cached files ≈ your PHP file count: If much lower, increase
max_accelerated_files
Step 6: Force OPcache Flush After Deployments
When deploying new PHP code, OPcache may serve the old compiled version until revalidate_freq expires:
# Reset entire OPcache (run as www-data)
sudo -u www-data php -r "opcache_reset(); echo 'OPcache cleared\n';"
# Graceful PHP-FPM reload (faster than full restart, no dropped requests)
sudo systemctl reload php8.2-fpm
Expected Performance Improvements
| Configuration | WordPress Requests/sec | TTFB |
|---|---|---|
| Default PHP 8.2 (no OPcache) | ~15 | ~400ms |
| OPcache enabled (defaults) | ~45 | ~150ms |
| OPcache tuned (this guide) | ~65 | ~100ms |
| OPcache + Redis object cache | ~80 | ~70ms |
| OPcache + Redis + Nginx page cache | ~3,000+ | ~5ms |
Getting Started
OPcache tuning is the highest-impact, zero-cost PHP performance improvement — configuration changes to an existing installation, no code changes required. All Ubuntu VPS plans at VPS.DO include PHP 8.2+ with OPcache available. Apply these settings, run the benchmark again, and compare — the improvement is typically 3–5× for WordPress sites.
Conclusion
PHP performance on a VPS is determined by three configuration decisions: OPcache memory allocation (eliminate recompilation), PHP-FPM process count (match to available RAM), and request-based worker recycling (prevent memory leaks). JIT adds benefit for CPU-intensive workloads. Together, these changes deliver 3–5× throughput improvement over default PHP configurations — the highest-ROI optimization available for PHP-based VPS hosting.