# opcache preload cuts the deploy bill by up to fourteen times — but five of seven frameworks do not hand it to you

> The footprint measurement left one line in its environment record: 'preload: none'. Same images, same route, same measurement — with preload on this time. Three variants: off, the framework's own file, and a uniform ceiling generated from Composer's classmap.

- Kind: Measurement
- Question: 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.
- 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%.
- Metrics: 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
- Measured on: 2026-08-22
- Confidence: High confidence
- Status: Current
- Programme: Language & runtime
- 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: PHP, opcache, Laravel, Symfony, CodeIgniter, Yii2, Phalcon, Laminas, Slim, Docker, nginx, Composer
- To reproduce: REPEATS=3 ./bench/preload.sh
- Source code: https://github.com/muhammetsafak/php-framework-bench
- Raw data: https://github.com/muhammetsafak/php-framework-bench/blob/main/results/2026-08-22/preload.json
- Raw data licence: https://github.com/muhammetsafak/php-framework-bench/blob/main/LICENSE
- Published: 2026-08-22
- Source: https://www.muhammetsafak.com.tr/en/research/opcache-preload-deploy-bill/
- Language: en-US
- Author: Muhammet Şafak

---
The [footprint measurement](/en/research/php-framework-footprint/) 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.

Source: bench/preload.sh, median of three repeats

|  | 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 |

| Candidate | no preload | official | classmap | Best gain | fpm cost |
| --- | --- | --- | --- | --- | --- |
| Laravel | 76.31 ms | none | 13.98 ms | 5.5× | +2,340 ms |
| Symfony | 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 | 2.68 ms | none | 1.99 ms | 1.3× | +43 ms |

fpm cost = the difference against the no-preload run in how long the container takes to answer. Negative values are measurement noise, not a gain.

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 files were probed for inside each image rather than listed by hand: the path is a framework decision and moves between versions.

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.

> **Result**
>
> Preload is not a setting, it is **maintenance work**. What it gives you depends
> not on how heavy the framework is but on whether somebody sat down and chose
> which classes get compiled up front. Where someone did, fourteen times. Where
> nobody did: either nothing, or a file you write yourself and that can break.

## 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 | 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 |

Classmap variant only; Symfony is absent because it broke.

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.

> **Caveat**
>
> "Cold" here means **an empty opcache**, not a cold page cache. Reading an image
> off disk for the first time is a separate cost this measurement does not
> include: a single-shot run put Symfony at 76.5 ms where the median of three is
> 35.58 ms. The figures come from one machine, one architecture (aarch64) and one
> PHP version. The `fpm cost` window includes nginx's own start-up — constant
> across the three variants, so the differences hold and the absolute numbers do
> not. And the `classmap` variant is not a configuration anyone deploys; it stands
> here as a ceiling.

## 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.
