
Every time PHP executes a script, it must read the source file from disk, parse the raw text into tokens, and then compile those tokens into an intermediate bytecode representation called opcodes. On a busy website this same compilation happens on every single request — even when the underlying source file has not changed in weeks. This repeated work is pure overhead, and it is one of the most impactful bottlenecks you can eliminate with almost no downside.
OPcache is a bytecode caching engine that ships with PHP 5.5 and later as a bundled extension. When OPcache is active, the compiled opcodes for each script are stored in shared memory. On subsequent requests, PHP skips the parse-and-compile step entirely and executes the cached opcodes directly. The result is a dramatic reduction in CPU usage and a proportional improvement in response latency — benchmarks routinely show a 2x to 5x throughput improvement on real-world applications after enabling OPcache with appropriate settings.
OPcache is different from an object cache like Redis or Memcached. Redis caches the results of expensive database queries or computed data structures at the application layer. OPcache operates one level below that, at the PHP engine itself, caching the compiled bytecode of your PHP files. Both caches complement each other and should be used together on any production server, but OPcache delivers the most benefit for PHP-heavy applications such as WordPress, Laravel, Magento, and Drupal.
On Ubuntu 22.04, OPcache is available as a separate package for each PHP version managed through the Ondrej Sury PPA. If you are running PHP 8.2 with PHP-FPM (the configuration used by CloudStick's EasyPHP installer), the package name is php8.2-opcache. The same naming convention applies to all other versions: php8.1-opcache, php8.3-opcache, and so on.
Run the following commands to install OPcache, verify the extension is loaded, and reload PHP-FPM so the change takes effect without dropping existing connections:
After installation, the package drops an ini snippet at /etc/php/8.2/mods-available/opcache.ini and a symlink under /etc/php/8.2/fpm/conf.d/. The default values shipped with the package are intentionally conservative — suitable for a development machine, but far from optimal for production. The next section covers every directive you should tune.
These instructions assume PHP-FPM is your SAPI. OPcache configuration is identical for CLI and Apache mod_php, but the ini file paths and reload commands differ. If you are using mod_php, replace php8.2-fpm with apache2 in the reload command. The opcache.enable_cli=0 directive should always remain disabled in CLI context to avoid stale cache issues during deployments.
OPcache exposes dozens of configuration directives, but six of them account for the vast majority of real-world performance impact. Open the OPcache ini file for your PHP version and set these values. For PHP 8.2 under PHP-FPM the file is /etc/php/8.2/fpm/conf.d/10-opcache.ini.
Here is what each directive controls and why the values above are appropriate for a production server:
opcache.memory_consumption — The total shared memory pool reserved for cached opcodes, in megabytes. The default of 128 MB fills up quickly on a WordPress multisite or a Laravel application with many packages. Setting it to 256 MB gives sufficient headroom for most workloads. You can monitor usage via opcache_get_status() and increase if the “used_memory” field stays above 80%.
opcache.interned_strings_buffer — A separate memory pool for interned strings (deduplicated string literals shared across all PHP processes). 16 MB is a safe starting point. Insufficient interned string memory shows up as a warning in the OPcache status output.
opcache.max_accelerated_files — The maximum number of PHP files that can be cached simultaneously. The value must be a prime number; OPcache will round up to the nearest prime automatically. A full WordPress installation with plugins and themes easily contains 10,000 to 15,000 PHP files; 20000 provides comfortable headroom.
opcache.revalidate_freq — How often, in seconds, OPcache checks whether a cached file has changed on disk. Setting this to 60 means a deployed file change may take up to one minute to propagate. For production sites that deploy infrequently this is fine. Pair it with a cache reset on deploy (covered below) for zero-latency deployments.
opcache.jit / opcache.jit_buffer_size — PHP 8.0 introduced a Just-In-Time compiler inside OPcache. The tracing mode provides the highest throughput for long-running request workloads. Allocating 100 MB of JIT buffer is sufficient for most PHP web applications. Note that JIT benefits CPU-bound code most — the gains on I/O-heavy workloads (database-heavy WordPress, for example) are more modest than on pure computation.
WordPress has unique characteristics that influence optimal OPcache settings. A typical WordPress installation loads between 200 and 500 PHP files per request — more on complex pages that trigger many plugins. With WooCommerce, Elementor, or a large plugin suite, that number can exceed 1,000 files per request. The OPcache cache hit rate is therefore critical: a miss means recompiling a file from scratch, and frequent misses negate the entire benefit of the cache.
One widely recommended WordPress-specific optimisation is to disable timestamp validation entirely in production and instead reset the cache programmatically on every deployment. This eliminates all filesystem stat() calls that OPcache normally performs to check for file changes, reducing per-request overhead:
Another important consideration for WordPress is the opcache.save_comments directive. It must be set to 1. WordPress, Doctrine, and many popular PHP frameworks rely on PHP docblock comments for annotation-based metadata. If OPcache strips comments during compilation (the behaviour when save_comments=0), these annotations become invisible to the application and you will see cryptic runtime errors or broken functionality.
For WordPress multisite networks with many subsites, consider increasing opcache.memory_consumption to 512 MB if your server has spare RAM. Each unique PHP file path is a separate cache entry, and multisite networks that activate different plugin sets per subsite can exhaust the default memory pool and cause silent cache evictions.
After applying your configuration and reloading PHP-FPM, confirm OPcache is active and healthy by creating a small PHP info script. Place the following in a password-protected or IP-restricted location — never expose it publicly on a production server:
A healthy OPcache deployment will show a cache hit rate above 95% after a brief warm-up period. If the hit rate is lower, the most common cause is an underpowered max_accelerated_files value — increase it and reload. If used memory is consistently above 90% of total allocated memory, increase opcache.memory_consumption.
For a graphical view, the open-source opcache-gui project by Andrew Collington provides a single-file dashboard that visualises memory usage, cache statistics, and per-file cache status. Drop it into a protected directory on your server and it gives you the same data as the script above, plus a history graph and a one-click reset button.
Even with a solid configuration, a few recurring issues trip up engineers deploying OPcache for the first time on production systems.
Stale code after deployment. If you set validate_timestamps=0 for performance, your deploy pipeline must explicitly flush the cache. The safest method on a PHP-FPM server is sudo systemctl reload php8.2-fpm — this performs a graceful reload that drains in-flight requests before workers are recycled, so there is no gap in service. Calling opcache_reset() via a web request is less reliable because it only resets the cache for the single FPM worker that handles that request.
OPcache appears enabled in php --version but not in phpinfo(). This usually means OPcache is loaded for the CLI SAPI but not for the FPM SAPI. Check that the symlink exists in both /etc/php/8.2/fpm/conf.d/ and /etc/php/8.2/cli/conf.d/. Also confirm that opcache.enable=1 is set (not commented out) in the relevant ini file.
Segmentation faults or PHP-FPM crashes after enabling JIT. JIT is stable in PHP 8.2, but certain third-party C extensions interact poorly with it. If you experience crashes after enabling JIT, disable it first (opcache.jit=off) and test whether the crashes disappear. Known problem areas include older builds of the Xdebug extension and some PHP Redis client extensions built against older libzstd versions.
Cache fills up and hit rate drops over time. This symptom — sometimes called “cache thrashing” — occurs when max_accelerated_files is too low or memory is exhausted. OPcache evicts entries using a least-recently-used algorithm, and with too little headroom it evicts files that are still being actively requested. The fix is to increase either memory_consumption or max_accelerated_files (or both) until the hit rate stabilises above 95% under production traffic. On a dedicated server with 8 GB or more of RAM, allocating 512 MB to OPcache is a conservative and entirely reasonable choice for a high-traffic WordPress site.

