OPcache is PHP's built-in bytecode cache. It compiles PHP files once and stores the compiled bytecode in shared memory, eliminating the need to parse and compile on every request. Here's how to configure it properly, with lessons from running DailyWatch in production.
How OPcache Works
Without OPcache:
Request -> Read .php file -> Parse -> Compile to bytecode -> Execute
With OPcache:
First request: Read .php file -> Parse -> Compile -> Store in shared memory -> Execute
Next requests: Load from shared memory -> Execute
The parse and compile steps are skipped entirely for cached scripts.
Recommended Configuration
Add to your php.ini or .user.ini:
; Enable OPcache
opcache.enable=1
opcache.enable_cli=0
; Memory allocation
opcache.memory_consumption=128 ; 128MB for bytecode cache
opcache.interned_strings_buffer=16 ; 16MB for interned strings
opcache.max_accelerated_files=10000 ; Max cached scripts
; Revalidation
opcache.revalidate_freq=60 ; Check for file changes every 60s
opcache.validate_timestamps=1 ; Check if files changed (set to 0 in prod for max speed)
; Optimization
opcache.optimization_level=0x7FFEBFFF ; Enable all optimizations
opcache.save_comments=1 ; Keep doc comments (needed by some frameworks)
opcache.fast_shutdown=1 ; Use fast shutdown sequence
; JIT (PHP 8.0+)
opcache.jit=1255 ; Tracing JIT
opcache.jit_buffer_size=64M ; JIT code buffer
Verifying OPcache Is Active
function checkOpcache(): void {
if (!function_exists('opcache_get_status')) {
echo "OPcache extension not loaded\n";
return;
}
$status = opcache_get_status(false);
if (!$status['opcache_enabled']) {
echo "OPcache is disabled\n";
return;
}
$memory = $status['memory_usage'];
$stats = $status['opcache_statistics'];
echo "OPcache Status:\n";
echo " Memory used: " . round($memory['used_memory'] / 1048576, 2) . " MB\n";
echo " Memory free: " . round($memory['free_memory'] / 1048576, 2) . " MB\n";
echo " Cached scripts: {$stats['num_cached_scripts']}\n";
echo " Hit rate: " . round($stats['opcache_hit_rate'], 2) . "%\n";
echo " Misses: {$stats['misses']}\n";
}
Production vs Development Settings
// Development - prioritize seeing changes immediately
$devSettings = [
'opcache.revalidate_freq' => 0, // Always check for changes
'opcache.validate_timestamps' => 1, // File modification detection ON
];
// Production - prioritize performance
$prodSettings = [
'opcache.revalidate_freq' => 300, // Check every 5 minutes
'opcache.validate_timestamps' => 0, // Never check (requires manual reset)
];
If validate_timestamps=0, you MUST reset OPcache after deploying new code:
// Call after deploy
opcache_reset();
Cache Warming
Pre-compile your most-used files after a cache reset:
function warmOpcache(string $directory): int {
$compiled = 0;
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($directory)
);
foreach ($iterator as $file) {
if ($file->getExtension() === 'php') {
if (opcache_compile_file($file->getRealPath())) {
$compiled++;
}
}
}
return $compiled;
}
// Warm after deploy
$count = warmOpcache('/var/www/html/app/');
echo "Pre-compiled {$count} PHP files\n";
Performance Impact
Measured on dailywatch.video:
| Metric | Without OPcache | With OPcache | Improvement |
|---|---|---|---|
| Time to First Byte (uncached) | 280ms | 160ms | 43% faster |
| PHP execution time | 95ms | 52ms | 45% faster |
| Memory per request | 4.2MB | 2.8MB | 33% less |
| Requests/sec (ab -n1000) | 85 | 155 | 82% more |
OPcache is the single easiest performance optimization you can make for a PHP application. It requires no code changes, just configuration. If you're running PHP in production without OPcache enabled, you're leaving significant performance on the table.
Check your current status with phpinfo() and configure accordingly. The defaults are intentionally conservative — tuning them for your specific application yields even better results.
Top comments (0)