opcache preload cuts the deploy bill by up to fourteen times — but five of seven frameworks do not hand it to you
With `opcache.preload` on, how long is the first request seven PHP frameworks serve after a deploy, what does the gain cost, and who can actually have it?
Finding
Preload shortens the cold first request by between 3.5× and 14.2×: Symfony drops from 35.58 ms to 2.50 ms, down to Phalcon's bare figure. But only two of the seven candidates — Symfony and CodeIgniter — publish a preload file of their own; for the other five the gain sits on the table waiting for the user to write one. Writing one is not as easy as it looks: a preload generated blindly from the classmap never brings Symfony up at all, and on CodeIgniter it does worse (5.29 ms) than the hand-picked official file (3.13 ms). And the cost does not vanish: Laravel's classmap preload takes the 62 ms it saves each visitor and writes it back as 2,340 ms of php-fpm start-up.
- Symfony, cold first request
- 2.50 ms −93%
- Candidates shipping a preload file
- 2 / 7
- Laravel classmap, fpm cost
- +2,340 ms
- Hand-picked vs compile-everything
- 3.13 → 5.29 ms
Method
The same images as the footprint and load measurements, the same PHP 8.4 build, the same nginx + php-fpm stack. Nothing was rebuilt: opcache reads `conf.d` in order, so a `02-*.ini` mounted at run time overrides the `00-bench-opcache.ini` baked into the image. Three variants per candidate — no preload, the framework's own file (found by probing the container, never hard-coded), and a uniform file that compiles every class in Composer's classmap through `opcache_compile_file`. Each variant got a fresh container, and what was measured is **the first request that container ever serves**: the readiness poll is the measurement, because a separate poll spends the first request and leaves a warm one behind. Three repeats, median reported. Preload's cost is measured as the time until php-fpm answers with a 200; that window includes nginx's own start-up, but it is the same constant in all three variants. Cross-check: the `no preload` variant reproduces the cold figures of the independent run published on 20 August to within 6%.
- Measured on
- Published
measured yesterday
Environment
- PHP
- 8.4.24 NTS · opcache on · JIT off
- opcache
- validate_timestamps=0 · 256 MB · preload on/off per variant
- Server
- nginx 1.29.1 + php-fpm · pm=static · 8 workers
- Hardware
- Apple M4 Pro · 12 cores · 24 GB · macOS 26.6.1
- Virtualisation
- Docker Desktop 29.7.2 · aarch64
- Repeats
- 3 · median reported
- Baseline check
- within 6% of the 2026-08-20 run
Technologies
To reproduce
REPEATS=3 ./bench/preload.sh The footprint measurement left a number behind: on the first request a fresh container serves, Phalcon takes 2.2 ms and Laravel 72.2 ms — thirty-three times. That is the compile bill the first visitor after a deploy pays. The same record’s environment carried one more line: preload: none.
This record closes that line. One variable.
The gain
First request after a deploy, with preload off and on
Each candidate's best variant is shown: their own official file for Symfony and CodeIgniter, the classmap ceiling for the rest.
- no preload
- preload on
ms lower is better Source: bench/preload.sh, median of three repeats
Data table
| Series | Laravel | Symfony | Laminas | CodeIgniter | Yii2 | Slim | Phalcon |
|---|---|---|---|---|---|---|---|
| no preload | 76.31 ms | 35.58 ms | 26.7 ms | 20.75 ms | 16.98 ms | 7.46 ms | 2.68 ms |
| preload on | 13.98 ms | 2.5 ms | 3.81 ms | 3.13 ms | 4.8 ms | 1.86 ms | 1.99 ms |
The chart is drawn in the browser; the table below carries the same data.
| Candidate | no preload | official | classmap | Best gain | fpm cost |
|---|---|---|---|---|---|
| Laravel | 76.31 ms | none | 13.98 ms | 5.5× | +2,340 ms |
| Symfony the largest gain | 35.58 ms | 2.50 ms | broke | 14.2× | −40 ms |
| Laminas | 26.70 ms | none | 3.81 ms | 7.0× | −7 ms |
| CodeIgniter | 20.75 ms | 3.13 ms | 5.29 ms | 6.6× | −12 ms |
| Yii2 | 16.98 ms | none | 4.80 ms | 3.5× | +13 ms |
| Slim | 7.46 ms | none | 1.86 ms | 4.0× | +28 ms |
| Phalcon classes live in the C extension | 2.68 ms | none | 1.99 ms | 1.3× | +43 ms |
The footprint record’s thirty-three-fold gap closes under preload: Symfony lands at 2.50 ms, Phalcon’s bare figure. The difference was never the framework’s weight; it was opcache being cold.
But five of them do not have one
| Candidate | Official preload | Where |
|---|---|---|
| Symfony | yes | var/cache/prod/*.preload.php, produced by warm-up |
| CodeIgniter | yes | preload.php, in the starter template |
| Laravel | no | — |
| Yii2 | no | — |
| Laminas | no | — |
| Slim | no | — |
| Phalcon | no | not needed |
The second column says more than the first. The gain exists, but in five of the seven it is waiting for the user to write it — and the candidate with the most to gain, Laravel at 76.31 ms, is one of those five.
”Compile everything” is not the answer
For a framework without an official file the obvious fix is to walk Composer’s classmap and compile the lot. The measurement refutes that in two different ways.
On Symfony the container never comes up. Compiling 4,721 files at load time runs into classes that resolve environment variables:
NOTICE: PHP message: [critical] Uncaught Exception:
Environment variable not found: "DEFAULT_URI".
Preload runs in php-fpm’s master process; something dying there means the
service never starts at all. The harness records it as ready: false.
On CodeIgniter it is worse than the official file. 3.13 ms official against 5.29 ms for the classmap. Forty-odd hand-picked classes beat 479 compiled blindly: preload fills shared memory, and classes nobody needs take up room.
The cost moves, it does not disappear
Preload takes the compile work out of the request and puts it into php-fpm’s start-up. On small preloads that cost is unmeasurable; on the large one it is unmistakable:
| Candidate | Files compiled | Time to first answer | Difference |
|---|---|---|---|
| Laravel paid on every deploy | 4,721 | 2,755 ms | +2,340 ms |
| Laminas | 1,195 | 352 ms | −7 ms |
| Yii2 | 992 | 352 ms | +13 ms |
| CodeIgniter | 479 | 324 ms | −12 ms |
| Slim | 138 | 359 ms | +28 ms |
| Phalcon | 3 | 362 ms | +43 ms |
Laravel’s classmap preload takes 62 ms from every visitor and writes 2.3 seconds into the deploy. On a service with steady traffic that is a good trade — the first visitor pays once and nobody after them pays at all. On a service that deploys ten times a day it inverts: twenty-three seconds of start-up delay against 62 ms for a few hundred visitors.
Phalcon turned into the control group
Only 3 files were compiled out of Phalcon’s classmap — its classes are in a C extension, not in PHP. Its gain is also the smallest at 1.3×. That is the check on what the measurement measures: if preload removes PHP source compilation, it should do nothing for a candidate with no PHP source to compile. It did nothing.
Back to the footprint record
That record said disk size predicts nothing and that the one thing which really separates the candidates is the first request paid while opcache is cold. This measurement adds a condition to that sentence: opcache does not have to stay cold — but whether it does depends on whether your framework handed you a preload file.
Related posts
Building Applications with PHP in 2026: State of the Ecosystem
The real state of PHP in 2026: language maturity, ecosystem health, and why the 'it's dead' narrative is still wrong.
Is PHP Dead? The 2026 Picture, by the Numbers
A view from 18 years of PHP development — the 2026 state of PHP through W3Techs, Stack Overflow, JetBrains, and Packagist data, including the language's often-ignored weaknesses.