PHP preloading in PHP 8 loads core classes and functions into memory when PHP-FPM starts, thereby significantly shortening the path to the actual application logic. I'll show you how I Preloading combine it with OPcache, where it provides measurable performance gains, and how to reliably integrate it into builds and deployments.
Key points
Before I go into more detail, I'll summarize the most important aspects and organize them for practical application. I'll briefly explain the relationship between OPcache and preloading, and explain why this effect is particularly noticeable in large frameworks. After that, I’ll discuss hard numbers regarding latency, throughput, and autoloading to ensure expectations remain realistic. I’ll also explain when I use preloading and when I omit it to save effort. Finally, I’ll provide tips on configuration, scripts, testing, and a clean Restart in operation.
- Mechanics: Precompiled classes/functions remain available throughout the process.
- Performance: minus 5–15 % TTFB, plus 30–50 % RPS possible.
- Autoloading: Save 10–16 ms per request.
- Selection: Include only stable core modules and the framework base.
- Deployment: Changes require a restart of FPM with a plan.
OPcache vs. Preloading: A Brief Overview
OPcache compiles files into bytecode the first time they are called and stores the bytecode in memory, while Preloading runs once, in a targeted manner, at startup. I use preloading to compile core classes in advance and keep them permanently in a persistent section of OPcache. This makes essential symbols directly available without the need for autoloading, file parsing, or `include`/`require` statements. While the standard OPcache may discard entries under memory or time constraints, the preloaded elements remain intact. This allows me to save I/O, eliminate cold-start slowdowns, and reduce CPU time in the early Bootstrap-The era of large apps.
How Preloading Shortens the Request Cycle
A typical request first loads hundreds of files before the controller and business logic run, and this is exactly where Preloading. I pre-cache core components of frameworks like Symfony or Laravel, thereby eliminating the need to repeatedly parse many files. This often reduces the Time To First Byte by 5–15 % and provides more headroom for actual logic. Autoloading chains are eliminated for core classes, which is particularly noticeable with response times under 200 ms. Under load, the number of requests per second increases because more CPU time is allocated to the actual Application benefits.
When Preloading Really Works
I enable preloading especially for large framework stacks, APIs, and e-commerce systems with many classes, since cold starts can be costly in those cases. In such environments, 30–50 % provide additional benefits RPS real benefits, especially if the hardware is to remain unchanged. Small scripts or simple pages with few includes hardly benefit at all because the overhead is minimal. WordPress comes out on top when many plugins and custom libraries are running in the background. In all cases, it’s important to carefully select which files to load Files, otherwise the cache will use up memory unnecessarily.
Limits and Pitfalls in Everyday Life
Preloading remains static until I restart the FPM pool, and that is exactly what requires discipline in Deployment. As soon as I deploy modified preloaded files, running processes still see the old bytecode. That’s why I plan restarts carefully and don’t preload artifacts that change frequently, such as generated classes. I also pay close attention to OPcache memory and the maximum number of cached files to ensure nothing falls out of the cache. Anyone interested in delving deeper into inconsistent caches and restarts can find background information on the OPcache validation, which I always take into account in large setups.
OPcache and Preload Configuration in PHP 8
To get off to a good start, I enable OPcache, configure the memory size, and define the preload script along with the user account to prevent any permission issues. Important settings include zend_extension, opcache.enable, memory_consumption, max_accelerated_files, and the paths to opcache.preload and opcache.preload_user. I aim for consistent settings per FPM pool, because mixing settings can quickly lead to debugging issues. I use the following parameters as a guideline and adjust them based on project size and Traffic ... Anyone who wants to explore these options in more depth will find practical tips on OPcache configuration, which I check during every fine-tuning session.
| Setting | Example value | Effect |
|---|---|---|
| opcache.enable | 1 | Activated OPcache worldwide. |
| opcache.memory_consumption | 256–512 | Reserves MB for bytecode and symbols. |
| opcache.max_accelerated_files | 20000–100000 | Increases the number of cached files. |
| opcache.preload | /path/to/preload.php | Defines the Preload-script. |
| opcache.preload_user | www-data | Specifies the user who will run the program. |
Setting Up a Preload Script
In preload.php, I explicitly list core classes or recursively compile selected directories using opcache_compile_file(). I start with the framework base and stable modules from src/ to achieve the highest hit rate in the hot path. Loading the entire vendor directory usually bloats the cache and increases Risk during deployment. It’s better to have a short whitelist for the framework core and a carefully controlled automatic integration of custom modules. By including comments and a version identifier in the script, I can keep track of everything and control restarts intentionally, rather than Coincidence to give up the field.
Measure, validate, readjust
I never enable preloading blindly; instead, I first measure baseline metrics for TTFB, CPU load, memory, and RPS. Then I adjust the selection of files and check again to see if autoloading and file accesses decrease. Simple request logs quickly show how many includes are eliminated and where there are still Bottle necks monitor. To assess performance under load, I use repeatable benchmarks, such as identical scenarios for each build. If the numbers look good, I lock the preload list and document the process in CI/CD.
Integrating Preloading into DevOps and Deployment Processes
I integrate the preload script into the build, have the artifacts checked, and trigger a scheduled FPM restart at the end. Rollbacks always take the pinned preload version into account so that old processes remain consistent. Blue/Green or Canary deployments reduce the risk while I implement the new Configuration rollout. During maintenance windows, I prioritize short-lived pools and postpone write-intensive operations until the nodes are back up to speed. This way, I keep latency spikes to a minimum and prevent bytecode race conditions on Servers.
Hosting Strategy: When Server Setup Makes All the Difference
A high-performance stack with PHP 8.x, fast NVMe, sufficient RAM, and appropriate OPcache limits makes preloading shine. I make sure that FPM pools are configured consistently and that there’s enough buffer space for persistent bytecode. Depending on the project phase, I adjust the number of processes, memory, and max-files to avoid cache garbage. When switching versions, I check for side effects, because changes to the engine’s internals can affect bytecode may have. Those who effectively coordinate setup and versions will see measurable benefits; notes on PHP Version and Hosting I use this as a guideline for sizing.
Practical Checklist for Projects
I start with a preload pilot on staging and gather reliable before-and-after metrics. Then I select the 50–200 hottest classes from the framework and core modules, rather than loading the entire vendor. I document restarts, link preload versions to builds, and roll out updates in groups. For maintenance, I keep scripts, OPcache parameters, and metrics in the repository so that every change remains traceable. With this approach, I achieve shorter TTFB, higher RPS and smoother load curves without any surprises.
Fine-tuning details that are often overlooked
In addition to the core parameters, it’s worth taking a look at a few adjustment factors that help stabilize the result:
- opcache.interned_strings_buffer: Allow for 16–64 MB. Large frameworks benefit because many identical strings (namespaces, method names) are stored in memory only once.
- opcache.save_comments: Leave this set to 1 if you are using attributes or annotations. If you omit comments, you risk unexpected behavior with reflection and validators.
- opcache.validate_timestamps: In production, this is often set to 0 so that OPcache doesn't constantly check the file system. When combined with preloading, this makes sense because changes require a restart anyway.
- opcache.revalidate_freq: If `validate_timestamps=1` (e.g., Staging), increase the frequency (e.g., 60) to reduce the load on the file system.
- opcache.jit and jit_buffer_size: JIT rarely provides a significant performance boost for web workloads, but it does consume memory. I keep JIT set to "conservative" or turn it off unless it's demonstrably necessary, so as not to cannibalize preload memory.
Selecting Suitable Candidates
The selection determines effectiveness and stability. I take a data-driven approach to this:
- Include Statistics: In the access log or profiler (Xdebug/Blackfire), I can see which files are loaded most frequently per request.
- Composer Class Map: With the optimized autoloader (dump-autoload -o), I have a good foundation for identifying stable namespaces from core and src.
- Framework Core: In Symfony, for example, HttpKernel, EventDispatcher, routing, and the DI container foundation; in Laravel, Foundation, Support, and parts of Illuminate.
- Custom Base Modules: Value objects, utility layers, central interfaces, and traits that nearly every request uses.
Do not preload: dynamically generated artifacts (proxies, compiled containers, caches), domain classes that change frequently during active development, or rarely used admin modules.
Example: Robust Preload Script
A quick, maintainable approach that compiles only the desired sections and logs the results clearly:
<?php
// preload.php v1.3 - Auswahl nur stabiler Kernmodule
$root = __DIR__;
$paths = [
$root . '/src/Domain',
$root . '/src/Application',
$root . '/vendor/symfony/http-kernel',
$root . '/vendor/symfony/event-dispatcher',
$root . '/vendor/illuminate/support',
];
// Hilfsfunktion: rekursiv PHP-Dateien kompilieren
function preload_dir(string $dir): void {
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS)
);
foreach ($it as $file) {
if ($file->isFile() && $file->getExtension() === 'php') {
@opcache_compile_file($file->getPathname());
}
}
}
foreach ($paths as $path) {
if (is_dir($path)) {
preload_dir($path);
}
}
// Einzeldateien, die nicht in obigen Ordnern liegen
$single = [
$root . '/src/Kernel.php',
$root . '/src/Infrastructure/Bootstrap.php',
];
foreach ($single as $file) {
if (is_file($file)) {
@opcache_compile_file($file);
}
}
// Optional: Marker in die Error-Log ausgeben
error_log('[preload] completed at ' . date(DATE_ATOM));
Important: I use absolute paths, avoid side effects caused by `require` statements or included files in the preload script, and keep the list consistent. `opcache_compile_file()` compiles the file without executing it—this prevents Bootstrap code from running during the preload phase.
Framework Features
In Symfony, I combine preloading with cache warmup: First, I build the container and route cache; then I compile stable core classes. Proxies and the generated container itself are left out, since their filenames and contents can change with each build. In Laravel, the same applies to config, route, and view caches: They help with startup, but aren’t good candidates for preloading because they change frequently. WordPress benefits when I select the hot paths of large plugins (CPT registration, shortcode parser, query utilities) without including the entire `vendor` directory.
Safety and Rights
Because preloading runs under the `opcache.preload_user` account when FPM starts, I make sure this user has read access to all files to be precompiled. I only preload signed, verified code from the build artifact. Experimental or untested packages have no place in the preload process, as a single error could throw the entire pool off track. In multi-tenant scenarios, I separate preload scripts by pool to prevent leaks between projects.
Diagnosis and monitoring
For day-to-day operations, I need quick checks:
- phpinfo(): Shows whether preloading is enabled and which file is set as `opcache.preload`.
- opcache_get_status(): Displays memory usage, cached scripts, and wasted memory; I pay particular attention to the remaining free MB and the number of accelerated files.
- Logs: The preload script can write a brief success message to the error log; if there are errors, I see path or permission issues there.
- Metrics: I monitor TTFB, CPU load, and the 95th and 99th percentiles of response times before and after restarts to detect regressions early on.
Common Pitfalls
- Reload vs. Restart: An FPM-reload isn't enough for the preload changes to take effect. I'm planning a full restart of the pool.
- Storage shortage: If `opcache.memory_consumption` is set too low, OPcache will displace normal scripts or reject new entries. I allocate a generous amount of memory and check after the warm-up to see how much buffer remains.
- Too many choices: A full vendor preload increases memory usage but rarely improves the hit ratio. I remain selective and monitor the results.
- Side Effects of Preloading: Never include files with global code that establishes database connections or relies on environment variables. I use opcache_compile_file() instead of require.
- Inconsistent Paths: Relative paths may not work in container or chroot environments. I strictly use absolute paths.
Container and Orchestration Setup
In containers, preloading restarts with every new pod or container. This is good for consistency, but it can slow things down during the first minute. Here's how I solve that:
- Readiness Test: The pod does not signal „ready“ until the preload script has finished running and the OPcache has been populated stably.
- Warm-up Request: After startup, I send targeted requests to hot endpoints to initialize even those paths that aren't preloaded but are frequently accessed.
- Throttled Rolling Update: Run small batches for new pods so that not all instances are undergoing a cold start at the same time.
Rollback and contingency plan
If a preload change causes problems, I want to be able to quickly revert it:
- Versioned Preload Script: Each build number corresponds to a specific preload version.
- Quick Toggle: I have a configuration option available that temporarily disables opcache.preload until the cause is determined.
- A Targeted Fresh Start: First, small pools or a Canary node, then the remaining instances in stages.
What Preloading Doesn't Solve
Preloading speeds up the PHP bootstrap, but it is no substitute for database optimization, HTTP response caching, or asynchronous processes. If external services or queries account for the majority of the time spent, preloading has only a limited effect. In such cases, I prioritize query tuning, response caching, and queue-based workflows—preloading then serves to round out the overall system.
Realistic Expectations for Each Project Phase
- Greenfield/Early Development: I often skip preloading in local setups so I can see changes without having to restart. On staging, I test selectively.
- Feature Freeze: Preloading is now paying off—bundle stable core modules and validate target values for TTFB and RPS through load testing.
- Long-term operation: Once a quarter, I check whether the preload list still matches the hot paths. New modules are added only after they have been tested.
Quick Overviews for Fast PHP 8 Projects
Preloading added OPcache This is ideal because it makes key classes and functions permanently available when the process starts. In large projects, I use this to reduce autoloading costs, file accesses, and parsing overhead, which often lowers TTFB by 5–15 %. For API and e-commerce workloads, throughput can increase by as much as 30–50 %, provided the database and external services can keep up. I achieve the greatest gains through careful selection, well-configured OPcache parameters, load testing, and scheduled restarts. Those who take these points to heart will get the most out of PHP 8 It consistently delivers higher speeds and reliably maintains low response times even during peak periods.


