Docs / Performance Optimization / OPcache Preloading PHP 8

OPcache Preloading PHP 8

By Admin · Mar 15, 2026 · Updated Apr 23, 2026 · 362 views · 5 min read

OPcache preloading, introduced in PHP 7.4 and refined in PHP 8.x, loads specified PHP files into shared memory when PHP-FPM starts. These files are then available to all requests without any file system access or compilation — they are simply there in memory. For framework-heavy applications like Laravel or Symfony, preloading can reduce request overhead by 15-30%.

How Preloading Works

Without preloading, every PHP request must:

  1. Check if the file is in OPcache (memory lookup)
  2. If not, read the file from disk
  3. Compile PHP to opcodes
  4. Store opcodes in OPcache
  5. Execute opcodes

With preloading, steps 1-4 are eliminated for preloaded files. The opcodes are loaded into memory when PHP-FPM starts and persist until restart.

Enabling Preloading

; php.ini
opcache.preload = /var/www/app/preload.php
opcache.preload_user = www-data

; Required OPcache settings
opcache.enable = 1
opcache.memory_consumption = 256
opcache.max_accelerated_files = 20000
opcache.interned_strings_buffer = 32

Laravel Preload Script

<?php
// /var/www/app/preload.php
// Preload Laravel framework files that are used on every request

require_once __DIR__ . '/vendor/autoload.php';

$files = [
    // Core Laravel files loaded on every request
    __DIR__ . '/vendor/laravel/framework/src/Illuminate/Container/Container.php',
    __DIR__ . '/vendor/laravel/framework/src/Illuminate/Foundation/Application.php',
    __DIR__ . '/vendor/laravel/framework/src/Illuminate/Http/Request.php',
    __DIR__ . '/vendor/laravel/framework/src/Illuminate/Http/Response.php',
    __DIR__ . '/vendor/laravel/framework/src/Illuminate/Routing/Router.php',
    __DIR__ . '/vendor/laravel/framework/src/Illuminate/Routing/Route.php',
    __DIR__ . '/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php',
    __DIR__ . '/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php',
    __DIR__ . '/vendor/laravel/framework/src/Illuminate/Support/Collection.php',
    __DIR__ . '/vendor/laravel/framework/src/Illuminate/Support/Str.php',
    __DIR__ . '/vendor/laravel/framework/src/Illuminate/Support/Arr.php',
    __DIR__ . '/vendor/laravel/framework/src/Illuminate/View/View.php',
    __DIR__ . '/vendor/laravel/framework/src/Illuminate/Session/Store.php',
];

// Application models and frequent controllers
$appFiles = glob(__DIR__ . '/app/Models/*.php');
$files = array_merge($files, $appFiles);

$count = 0;
foreach ($files as $file) {
    if (is_file($file)) {
        try {
            opcache_compile_file($file);
            $count++;
        } catch (Throwable $e) {
            // Log but don't fail — some files may have unresolvable dependencies
            error_log("Preload failed for {$file}: " . $e->getMessage());
        }
    }
}

error_log("Preloaded {$count} files");

Symfony Preload Script

<?php
// Symfony generates a preload file automatically
// After warmup, use the generated file:
// opcache.preload = /var/www/app/var/cache/prod/App_KernelProdContainer.preload.php

// To generate:
// php bin/console cache:warmup --env=prod

// Or create a custom one:
$files = require __DIR__ . '/var/cache/prod/preload_classes.php';
foreach ($files as $file) {
    opcache_compile_file($file);
}

WordPress Preload Script

<?php
// /var/www/wordpress/preload.php
$files = [
    __DIR__ . '/wp-includes/class-wp.php',
    __DIR__ . '/wp-includes/class-wp-query.php',
    __DIR__ . '/wp-includes/class-wpdb.php',
    __DIR__ . '/wp-includes/formatting.php',
    __DIR__ . '/wp-includes/functions.php',
    __DIR__ . '/wp-includes/plugin.php',
    __DIR__ . '/wp-includes/option.php',
    __DIR__ . '/wp-includes/taxonomy.php',
    __DIR__ . '/wp-includes/post.php',
    __DIR__ . '/wp-includes/meta.php',
    __DIR__ . '/wp-includes/cache.php',
    __DIR__ . '/wp-includes/http.php',
    __DIR__ . '/wp-includes/rest-api.php',
];

// Preload active theme functions
$theme = 'your-theme';
$themeFiles = glob(__DIR__ . "/wp-content/themes/{$theme}/*.php");
$files = array_merge($files, $themeFiles);

foreach ($files as $file) {
    if (is_file($file)) {
        opcache_compile_file($file);
    }
}

Auto-Generating Preload Lists

<?php
// Use OPcache status to find most-used files
// Run this after the application has been serving traffic

$status = opcache_get_status(true);
$scripts = $status['scripts'];

// Sort by hits (most frequently accessed files)
usort($scripts, fn($a, $b) => $b['hits'] - $a['hits']);

// Generate preload file for top 200 files
$preloadFile = "<?php\n// Auto-generated preload list\n";
$count = 0;
foreach (array_slice($scripts, 0, 200) as $script) {
    $path = $script['full_path'];
    // Skip test files and dev dependencies
    if (strpos($path, '/tests/') !== false) continue;
    if (strpos($path, '/vendor/bin/') !== false) continue;

    $preloadFile .= "opcache_compile_file('{$path}');\n";
    $count++;
}

file_put_contents('/var/www/app/preload-generated.php', $preloadFile);
echo "Generated preload for {$count} files\n";

Monitoring Preloading

<?php
// Check preload status
$status = opcache_get_status();

echo "Preload enabled: " . ($status['preload_statistics']['enabled'] ? 'Yes' : 'No') . "\n";
echo "Preloaded scripts: " . $status['preload_statistics']['scripts'] . "\n";
echo "Preloaded memory: " . round($status['preload_statistics']['memory_consumption'] / 1024 / 1024, 2) . " MB\n";
echo "Preloaded functions: " . $status['preload_statistics']['functions'] . "\n";
echo "Preloaded classes: " . $status['preload_statistics']['classes'] . "\n";

// Full OPcache status
echo "\nOPcache memory used: " . round($status['memory_usage']['used_memory'] / 1024 / 1024, 2) . " MB\n";
echo "OPcache memory free: " . round($status['memory_usage']['free_memory'] / 1024 / 1024, 2) . " MB\n";
echo "Cache hit rate: " . round($status['opcache_statistics']['opcache_hit_rate'], 2) . "%\n";

Deployment Considerations

# Preloaded files persist in memory until PHP-FPM restarts
# After deployment, you MUST restart PHP-FPM to reload preloaded files

# Deployment script
#!/bin/bash
cd /var/www/app
git pull origin main
composer install --no-dev --optimize-autoloader
php artisan config:cache
php artisan route:cache
php artisan view:cache

# Graceful restart to reload preloaded files
sudo systemctl reload php8.3-fpm

# WARNING: opcache.validate_timestamps should be 0 in production
# This means OPcache won't detect file changes — you MUST restart FPM

Common Pitfalls

  • Circular dependencies: Preloading file A that depends on file B which depends on A will fail. Order matters or use opcache_compile_file() which handles this better than require.
  • Memory usage: Preloading too many files wastes shared memory. Focus on files used by every request.
  • Stale code: Forgetting to restart FPM after deployment means the old preloaded code is still served.
  • Dev environment: Disable preloading in development — you want file changes to take effect immediately.

Summary

OPcache preloading is a free performance boost for PHP applications. By loading framework and frequently-used application files into shared memory at startup, you eliminate per-request file I/O and compilation overhead. Start with the framework core files, add your most-hit application files, and use OPcache statistics to refine the list over time. The typical improvement is 15-30% reduction in request processing time, with the biggest gains on I/O-constrained VPS instances.

Was this article helpful?