PHP OPcache and Realpath Cache Tuning: Cut WordPress CPU Usage on Your VPS

Every PHP request compiles the requested script from source unless the compiled bytecode is already cached in memory. A typical WordPress installation with a dozen plugins contains 500–1000 PHP files, and without OPcache, PHP re-parses and re-compiles every single one on each request. That is a tremendous waste of CPU that shows up directly in server load and Time To First Byte (TTFB). OPcache stores compiled bytecode in shared memory, and the realpath cache eliminates repeated filesystem stat() calls. This tutorial walks through the exact settings that matter for WordPress on a VPS and how to verify they are working correctly.

What OPcache Does (and Why Defaults Are Not Enough)

OPcache is bundled with PHP and enabled by default in most distributions, but the default settings are tailored for generic PHP applications — not a plugin-heavy WordPress install. The three settings that control the essentials are:

  • opcache.memory_consumption — How much shared memory holds compiled scripts (default: 128 MB in PHP 8.x, but often lower in older distro packages)
  • opcache.max_accelerated_files — How many distinct files can be cached (default: 4000–10000 depending on distribution)
  • opcache.validate_timestamps / opcache.revalidate_freq — How often PHP checks if files changed on disk

If you’re running WordPress on a VPS, you need to tune these settings for your specific workload. Compare VPS plans with enough CPU headroom to get the most out of OPcache optimizations.

Step 1: Find Your PHP Configuration File

OPcache settings go in php.ini. For PHP-FPM (the most common setup with Nginx), the file is at:

# Find your PHP version
php -v | head -1

# Edit the FPM php.ini (adjust 8.x to your version)
sudo nano /etc/php/8.3/fpm/php.ini

For Apache with mod_php, edit the Apache php.ini instead (usually /etc/php/8.3/apache2/php.ini).

Step 2: Apply the Recommended OPcache Settings

Add or update these settings in your php.ini:

opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=10000
opcache.validate_timestamps=1
opcache.revalidate_freq=60
opcache.enable_cli=1
opcache.interned_strings_buffer=16
opcache.fast_shutdown=1

What each setting does:

  • memory_consumption=128 — A full WordPress install with plugins and a theme typically uses 64–128 MB of bytecode cache. Start at 128 and check usage with opcache_get_status().
  • max_accelerated_files=10000 — WordPress plus WooCommerce, page builders, and SEO plugins can easily exceed 10,000 files. Set this high enough that num_cached_scripts never reaches the limit.
  • revalidate_freq=60 — Recheck files every 60 seconds so deploys show up quickly without a stat() call on every request. If you deploy rarely, set this to 300 or higher.
  • enable_cli=1 — Cache bytecode for WP-CLI and cron jobs too. This speeds up scheduled tasks significantly.
  • interned_strings_buffer=16 — Caches repeated strings across PHP files. WordPress uses many duplicate strings (class names, function names, query strings), so this is a cheap win.
  • fast_shutdown=1 — Enables faster request shutdown sequence. Safe for most setups.

Step 3: Tune the Realpath Cache

The realpath cache stores resolved filesystem paths so PHP does not need to call stat() every time it include()s or require()s a file. WordPress does a lot of includes — every plugin and theme file — so this cache is important.

Add these settings to the same php.ini:

realpath_cache_size=4096k
realpath_cache_ttl=600

The default realpath cache size is often 4 MB (4096k), which is sufficient for most WordPress sites. If you have a very large plugin ecosystem (100+ plugins), consider increasing it to 8192k. The TTL of 600 seconds (10 minutes) means resolved paths are cached for 10 minutes before being re-checked — a good balance between freshness and performance.

Step 4: Restart PHP-FPM and Verify

# Restart PHP-FPM (adjust 8.x to your version)
sudo systemctl restart php8.3-fpm

# Verify the settings loaded
php -i | grep -E "opcache\.(enable|memory_consumption|max_accelerated|revalidate|interned_strings)"

Run a quick cache health check:

php -r '
$status = opcache_get_status(false);
echo "Memory used: " . round($status["memory_usage"]["used_memory"] / 1024 / 1024, 2) . " MB\n";
echo "Memory free: " . round($status["memory_usage"]["free_memory"] / 1024 / 1024, 2) . " MB\n";
echo "Cached scripts: " . $status["opcache_statistics"]["num_cached_scripts"] . "\n";
echo "Hits: " . $status["opcache_statistics"]["hits"] . "\n";
echo "Misses: " . $status["opcache_statistics"]["misses"] . "\n";
echo "Hit rate: " . round($status["opcache_statistics"]["hit_rate"], 2) . "%\n";
'

After a few requests to warm the cache, the hit rate should be above 95%. If it is below 90%, the cache is too small for your codebase — increase memory_consumption or max_accelerated_files.

Step 5: Monitor with a WordPress Admin Dashboard

For ongoing monitoring, install a free plugin like OPcache Manager or Query Monitor (which includes OPcache stats). The key figures to watch in the dashboard:

  • Hit rate — Should stay above 95% after the initial warm-up period
  • Memory used vs memory size — If the bar is nearly full, raise opcache.memory_consumption
  • Cached scripts vs max cached keys — If they are equal, raise max_accelerated_files

Set up a weekly cron job that logs these numbers to a file:

# Add to crontab -e
0 6 * * 1 php -r '
$s = opcache_get_status(false);
file_put_contents("/var/log/opcache_stats.log", date("Y-m-d H:i:s") . " | " .
  round($s["opcache_statistics"]["hit_rate"], 2) . "% | " .
  $s["opcache_statistics"]["num_cached_scripts"] . " scripts\n", FILE_APPEND);
'

Common Mistakes to Avoid

  • Memory too low: Setting memory_consumption to 32 or 64 MB on a plugin-heavy site — the cache fills, old scripts are evicted, and hit rate collapses.
  • validate_timestamps=0 with frequent deploys: Stale bytecode can serve old code after updates. Only disable validation if you clear the cache manually after each deploy.
  • Forgetting enable_cli=1: WP-CLI and cron jobs run uncached, slowing down scheduled tasks like backups and content updates.
  • Not restarting PHP-FPM: OPcache settings only load at PHP startup. Changing php.ini without restarting has no effect.
  • Ignoring WooCommerce: WooCommerce and page builders can push file counts past 10,000. Re-check num_cached_scripts after adding plugins.

OPcache and PHP 8.x JIT

PHP 8.x includes a JIT compiler that can speed up CPU-bound PHP code. For WordPress, however, JIT rarely moves the needle because most of the work is I/O (database queries, filesystem reads) and not tight CPU loops. JIT also consumes memory that could otherwise hold cached bytecode. Leave JIT off for typical WordPress installs and only enable it if profiling shows genuine CPU-bound PHP work.

Putting It All Together

OPcache tuning is one of the highest-return changes you can make on a WordPress VPS. It reduces CPU usage, lowers TTFB, and often delays the need for a larger plan. Pair it with page caching (like Nginx FastCGI Cache or a plugin like WP Rocket) and a CDN for the full performance stack.

If your current VPS struggles with PHP workloads even after tuning, browse our VPS provider comparisons to find a plan with more CPU headroom and fast NVMe storage.

Leave a Reply