Opcache Configuration for PHP 8.x Performance
In the African savanna, elephant herds have learned an essential survival principle: conserve energy by eliminating unnecessary repetition. When a herd needs water, they don’t rediscover water sources from scratch each time—they remember. The matriarch leads directly to known waterholes, allowing the herd to conserve precious resources for when they’re truly needed.
OPcache serves a similar purpose for your PHP applications. Before PHP 8.x, every request required parsing and compiling PHP scripts into opcode—repetitive work that consumed CPU cycles unnecessarily. OPcache stores precompiled script bytecode in shared memory, so subsequent requests skip the compilation step entirely. With PHP 8.x’s Just-In-Time (JIT) compiler built directly on top of OPcache, proper configuration becomes not just beneficial but essential for achieving optimal performance.
In this guide, we’ll walk through the essential php.ini settings to maximize your PHP 8.x application’s speed and efficiency. We’ll cover configuration options, explain the reasoning behind each setting, and help you avoid common pitfalls.
Prerequisites
Before we begin configuring OPcache, ensure you have:
- PHP 8.0 or later (OPcache is bundled with PHP, but JIT requires PHP 8.0+)
- Access to php.ini configuration file (typically
/etc/php/8.x/fpm/php.inior/etc/php.ini) - Basic understanding of PHP configuration and server administration
- Development environment for testing changes before deploying to production
We’ll assume you have a working PHP installation and can modify configuration files. If you’re using a shared hosting environment, contact your provider about OPcache availability.
Understanding OPcache
OPcache improves PHP performance by storing precompiled script bytecode in shared memory. Here’s how it works:
- First request: PHP loads a script, parses it into opcode (intermediate code), then executes it
- OPcache stores: The compiled opcode is saved in shared memory
- Subsequent requests: PHP retrieves the opcode directly from memory—skipping parsing and compilation
This typically reduces response times by 30-50% for typical applications, though actual gains depend on your application’s characteristics.
Note: Performance gains vary. Simple scripts with minimal dependencies may see smaller improvements, while complex applications with many files often benefit most.
Alternative Approaches and When OPcache Alone May Not Suffice
OPcache operates at the opcode level, but your application may benefit from additional caching strategies:
- APCu (Alternative PHP Cache User Cache): Provides key-value storage for application-level data caching—complements OPcache rather than replacing it
- External caching systems: Redis or Memcached for distributed caching across multiple servers
- Full-page caching: Varnish, Nginx FastCGI cache, or CDN caching for static or semi-static content
- OPcache user cache API: Since PHP 7.0, OPcache includes a user cache API that can sometimes replace APCu for simpler use cases
We’ll discuss when each approach makes sense in the troubleshooting section.
The Role of JIT in PHP 8.x
PHP 8 introduced the Just-In-Time (JIT) compiler, built directly on top of OPcache. JIT compiles frequently-executed opcode into native machine code at runtime. This particularly benefits CPU-intensive tasks—number crunching, image processing, complex algorithms.
JIT requires OPcache to be enabled. It adds some memory overhead and requires a warm-up period where the system learns which code paths are hot, but for compute-heavy applications, the performance gains can be substantial.
Key JIT considerations:
- Use tracing mode:
opcache.jit=tracingprovides balanced performance for most applications - Buffer size matters: The
opcache.jit_buffer_sizesetting controls how much memory JIT can use - Not for everyone: I/O-bound applications (typical web requests) may see minimal JIT benefits
Recommended Production Configuration
For production environments, we want to minimize disk reads and maximize cache hits. Add these settings to your php.ini file:
[opcache]
; Enable OPcache extension
opcache.enable=1
; Memory for compiled opcode (in megabytes)
; Range: 64-4096 depending on application size
opcache.memory_consumption=256
; Memory for interned strings (in megabytes)
; Range: 8-128; 32 is a solid starting point
opcache.interned_strings_buffer=32
; Maximum number of cached files
; Should exceed your total PHP file count
; Find your count: find /path/to/project -name "*.php" | wc -l
opcache.max_accelerated_files=20000
; Disable file timestamp validation for maximum performance
; Set to 1 if you need automatic change detection
opcache.validate_timestamps=0
; How often (in seconds) to check for file updates when validate_timestamps=1
; 0 means check on every request (development)
; Higher values (60-3600) reduce overhead in production with some staleness risk
opcache.revalidate_freq=0
; Optimize for production: don't check include_path for overrides
opcache.enable_file_override=1
; Preserve comments (required by some frameworks like Doctrine Annotations)
; Set to 0 for maximum performance if your code doesn't need them
opcache.save_comments=1
; JIT settings for PHP 8.0+
opcache.jit_buffer_size=128M
opcache.jit=tracing
Understanding the Key Trade-offs
Let’s examine the most important settings and their trade-offs:
Memory vs. Coverage: opcache.memory_consumption and opcache.max_accelerated_files control how much of your codebase fits in cache. Set these too low, and you’ll see frequent cache misses. Set them too high, and you waste memory that could serve other purposes. We recommend starting with 256MB memory and 20,000 files—then monitor your cache hit rate with opcache_get_status() to see if you need adjustments.
Freshness vs. Performance: opcache.validate_timestamps and opcache.revalidate_freq control how often OPcache checks if files have changed. In production, we typically set validate_timestamps=0 and manually reset the cache after deployments (using php -r 'opcache_reset();'). This eliminates file stat calls entirely. Of course, this means you must remember to reset the cache after each deployment—automate this in your deployment script.
Comments vs. Speed: opcache.save_comments removes PHP docblock comments from cached code when set to 0. This reduces memory usage and slightly improves performance. However, some frameworks—notably Doctrine with its annotation system—require these comments. Check your framework’s documentation before disabling.
JIT Considerations: The opcache.jit_buffer_size allocates memory for JIT-compiled code. Allocate at least 64M for JIT to be effective; 128M works well for most applications. Set opcache.jit=tracing for balanced performance; opcache.jit=function is an alternative that compiles individual functions on first call.
Development vs. Production Settings
The configuration above targets production. For development environments, you need different settings to see code changes immediately:
[opcache]
; Enable in development too
opcache.enable=1
; Keep most memory settings similar
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
; Check for file changes on every request
opcache.validate_timestamps=1
opcache.revalidate_freq=1
; Preserve comments for development tools
opcache.save_comments=1
; JIT typically unnecessary in development
; opcache.jit=0
These settings ensure your changes appear instantly but come with a performance cost—acceptable for development but not production.
Verifying OPcache is Working
After configuring OPcache, verify it’s active and caching effectively.
1. Check phpinfo()
The simplest method: create a file info.php with:
<?php phpinfo();
Load it in your browser and scroll to the “Zend OPcache” section. Look for:
- “Opcode Caching” => “Up and Running”
- “Cache Hits” and “Cache Misses” statistics
- Your configured settings displayed
Tip: Remove
info.phpafter verification—exposing phpinfo() publicly is a security risk.
2. Programmatic Status Check
For detailed metrics, create a script check-opcache.php:
<?php
header('Content-Type: application/json');
$status = opcache_get_status();
echo json_encode([
'enabled' => $status['opcache_enabled'],
'cache_hits' => $status['stats']['hits'],
'cache_misses' => $status['stats']['misses'],
'hit_rate' => $status['stats']['hits'] / ($status['stats']['hits'] + $status['stats']['misses'] + 1) * 100,
'cached_scripts' => $status['stats']['num_cached_scripts'],
'memory_usage' => $status['memory_usage'],
'memory_consumption' => $status['memory_consumption'],
], JSON_PRETTY_PRINT);
Run it from the command line:
$ php check-opcache.php
Example output:
{
"enabled": true,
"cache_hits": 15432,
"cache_misses": 234,
"hit_rate": 98.5123456789,
"cached_scripts": 1432,
"memory_usage": {
"used_memory": "64.5MB",
"free_memory": "191.2MB",
"wasted_memory": "0.3MB"
},
"memory_consumption": 256
}
What to look for:
enabledshould betruehit_rateabove 95% indicates good caching (lower suggests memory or file count limits too restrictive)cached_scriptsshould match your application’s PHP file count (approximately)used_memoryshould be less thanmemory_consumption
You can also query this via command line:
$ php -r 'print_r(opcache_get_status());'
3. Real-World Performance Testing
OPcache’s value shows in actual request times. Use profiling tools:
# Using ApacheBench (ab)
$ ab -n 1000 -c 10 http://your-app.test/
# Or wrk for more sophisticated testing
$ wrk -t4 -c100 -d30s http://your-app.test/
Compare request times before and after OPcache. Look for 30-50% reduction in average response time.
Common Configuration Scenarios
Let’s examine specific configurations for common use cases.
High-Traffic Laravel Application
Laravel applications typically have many files and benefit from generous OPcache settings:
opcache.memory_consumption=512
opcache.max_accelerated_files=30000
opcache.interned_strings_buffer=128
opcache.jit_buffer_size=256M
opcache.validate_timestamps=0
opcache.revalidate_freq=0
opcache.save_comments=0 ; Laravel doesn't need docblock comments
Note: Laravel’s route and config caches work independently of OPcache. OPcache caches PHP file compilation, not runtime data.
WordPress on Shared Hosting
Shared hosting often has memory constraints. Conservative settings:
opcache.memory_consumption=128
opcache.max_accelerated_files=5000
opcache.interned_strings_buffer=16
opcache.jit_buffer_size=64M
opcache.validate_timestamps=1 ; You may not control deployments
opcache.revalidate_freq=60 ; Check every minute if using timestamp validation
WordPress with many plugins needs more max_accelerated_files. Count installed plugin files and set accordingly.
API Server (CPU-Intensive Workloads)
If your API does significant computation (data transformation, image processing), enable JIT aggressively:
opcache.memory_consumption=512
opcache.max_accelerated_files=15000
opcache.jit_buffer_size=512M
opcache.jit=tracing
opcache.validate_timestamps=0
JIT memory scales with workload complexity; monitor memory usage if setting above 256M.
Troubleshooting
Even with careful configuration, issues arise. Here are common problems and their solutions.
Problem: OPcache reports “Configuration option ‘opcache.enable’ cannot be set at runtime”
Cause: You’re trying to enable OPcache via ini_set() in a script. OPcache must be enabled in php.ini and requires PHP restart.
Solution: Edit php.ini directly and restart your PHP process manager:
# For PHP-FPM
$ sudo systemctl restart php8.2-fpm
# For Apache with mod_php
$ sudo systemctl restart apache2
Problem: Cache hit rate is low (<80%)
Common causes:
opcache.max_accelerated_filestoo low for your codebaseopcache.memory_consumptioninsufficientopcache.validate_timestamps=1with frequent deployments causing regular cache flushes- Application uses too many unique files per request (try
opcache.max_wasted_percentagetuning)
Diagnosis:
$ php -r '$s=opcache_get_status(); echo "Cached: ".$s["stats"]["num_cached_scripts"]."\n"; echo "Memory used: ".round($s["memory_usage"]["used_memory"]/1024/1024, 2)."MB\n";'
If num_cached_scripts is much lower than your total PHP file count, increase opcache.max_accelerated_files. If memory is nearly full, increase opcache.memory_consumption.
Problem: Changes not appearing after deployment
Cause: With opcache.validate_timestamps=0, OPcache doesn’t check for file changes automatically.
Solution: Reset OPcache cache after each deployment:
$ php -r 'opcache_reset();'
Or in your deployment script:
# After uploading new code
$ curl -s http://your-app.test/clear-opcache.php
# Where clear-opcache.php contains: <?php opcache_reset();
Alternative: Set opcache.validate_timestamps=1 and opcache.revalidate_freq=60 to check every 60 seconds—this trades some performance for automatic updates.
Problem: Memory exhaustion errors
Symptom: PHP fatal errors like “Allowed memory size exhausted” or “opcache.memory_consumption too small”.
Cause: Your application has more code than fits in configured OPcache memory.
Solution: Increase opcache.memory_consumment in php.ini by 64-128MB increments, then restart PHP. Monitor memory usage:
$ php -r 'print_r(opcache_get_status()["memory_usage"]);'
Problem: JIT not improving performance
JIT benefits CPU-bound workloads. For typical web requests dominated by I/O and database queries, JIT overhead may outweigh benefits.
Diagnosis: Disable JIT and compare:
opcache.jit_buffer_size=0
Benchmark your application. If performance degrades with JIT enabled, your workload is likely I/O-bound—JIT won’t help much.
What to try: If you previously set opcache.jit_buffer_size=0, try 128M and benchmark again. Some applications see benefits even with mixed workloads.
Problem: Conflicts with APCu or other extensions
Both OPcache and APCu use shared memory. In rare cases, memory allocation conflicts occur.
Solution: Reduce opcache.memory_consumption to free shared memory for APCu. Typically, OPcache uses 128-512M, APCu 64-128M. Monitor total shared memory:
$ ipcs -m
Problem: Files not being cached
Check:
- OPcache disabled? Verify
opcache.enable=1 - File extensions? OPcache caches only
.phpfiles by default - File permissions? PHP must read files to cache them
- Memory full? Check
opcache.memory_consumptionnot exhausted - File count exceeded? Check
opcache.max_accelerated_filesnot hit
Diagnostic command:
$ php -r '$s=opcache_get_status(); echo "Cached files: ".count($s["scripts"])."\n"; echo "Memory: ".round($s["memory_usage"]["used_memory"]/1024/1024, 2)."MB used, ".round($s["memory_consumption"]/1024/1024, 2)."MB total\n";'
Advanced Configuration Options
Beyond the essentials, these settings offer fine control.
Memory Waste Management
; Reclaim memory from expired scripts (usually unnecessary)
opcache.revalidate_path=0
; Protect shared memory from accidental writes
opcache.protect_memory=1
; Allow large scripts to be cached despite fragmentation
opcache.max_wasted_percentage=5
Fast Shutdown
; Speeds up request shutdown by avoiding function cleanup
; Generally safe to enable
opcache.fast_shutdown=1
File Override Protection
; Prevents caching if include_path contains unexpected directories
; Useful for security-hardened environments
opcache.allow_file_override=0
These advanced settings rarely need adjustment. Stick with the recommended baseline unless you have specific needs.
Conclusion
Properly configured OPcache eliminates redundant PHP compilation—your herd doesn’t retrace its steps to the waterhole each time. We’ve covered:
- Production-ready settings for typical applications
- JIT configuration for compute-intensive workloads
- Verification techniques to confirm OPcache is working
- Troubleshooting for common configuration issues
- Scenario-specific tuning for Laravel, WordPress, and API servers
OPcache’s value compounds over time: a well-configured cache reduces server load, improves response times, and lets your infrastructure handle more traffic with the same hardware. Take a few minutes to review your php.ini file and apply these settings—your users and your servers will thank you.
Next Steps
- Monitor: Set up alerts for cache miss rates exceeding 5%
- Benchmark: Use
aborwrkto measure actual performance gains in your application - Automate: Include
opcache_reset()calls in your deployment scripts - Explore: Consider APCu for application-level caching when OPcache alone isn’t sufficient
For further reading:
Sponsored by Durable Programming
Need help with your PHP application? Durable Programming specializes in maintaining, upgrading, and securing PHP applications.
Hire Durable Programming