laragear/preload
Laragear Preload helps Laravel apps use PHP OPcache preloading. It generates a preload script, lets you include application and vendor files, and provides Artisan commands to build and refresh the preload list for faster bootstrapping.
laragear/preload generates a PHP preload script for OPcache (opcache.preload), so that a chosen set of files is compiled once when PHP-FPM boots instead of on every request. It is a build/deploy-time tool, not a request-time one — you will touch it on deploys, rarely in application code.
Install and publish the config.
composer require laragear/preload
php artisan vendor:publish --tag=preload.config
Open the published config/preload.php first. It defines what gets preloaded (autoloader, vendor paths, app paths, ignore rules), where the script is written (preload.php at the project root by default), and any discovery/tracing options.
Confirm OPcache is available in the SAPI you actually serve with:
php -i | grep -i opcache
Generate the script.
php artisan preload:generate
This writes the preload.php file. Look at the generated file — it is plain, readable PHP and is the single best documentation of what the package is doing.
Wire it into php.ini for the FPM pool:
opcache.preload=/var/www/app/preload.php
opcache.preload_user=www-data
Then restart/reload PHP-FPM. Preloaded scripts cannot be unloaded at runtime, so a reload is mandatory after every change.
Verify it took effect. Preload statistics are exposed by PHP itself:
opcache_get_status()['preload_statistics'];
// ['scripts' => [...], 'memory_consumption' => ..., 'classes' => n, 'functions' => n]
First use case: preload the handful of framework and app files that are included on every request (service providers, base classes, heavily used support classes), confirm via preload_statistics that they are actually in shared memory, and benchmark before/after. Start small and measurable; do not paste your entire vendor/ directory in on day one.
Curated list (default choice). Maintain an explicit, version-controlled set of files in config/preload.php or a dedicated list file. This is deterministic, reviewable in a diff, and safe to run in CI. Grow it iteratively: profile what is included on each request, add the top offenders, re-measure. This is the pattern most teams should end up with.
Discovery/tracing, then prune. Rather than reasoning about includes by hand, exercise the application so the package can observe which files were actually loaded, export that list, and turn it into the preload set. Practical workflow:
php artisan boot paths you care about.Deploy pipeline integration. Generate at build time, not on the live host if you can avoid it:
php artisan preload:generate as part of the release build, using the same PHP minor version as production.preload.php alongside the release (commit it, or produce it as a build artifact so it is diffable).systemctl reload php-fpm (or equivalent). The reload must come after the code swap, otherwise you preload the previous release's bytecode.opcache.preload and reload. This recovers instantly from a bad preload.Environment-scoped configuration. Preloading is a production/staging concern. Keep it off (or generating a trivial script) in local development, where you edit files constantly and want fast feedback rather than a stale shared-memory snapshot. Drive the file list from config so each environment can have a different set.
Programmatic extension. For lists that depend on runtime discovery (installed packages, feature flags, module registries), append entries from a service provider via the package's facade rather than hardcoding them in config. Keep this logic pure — no database calls, no HTTP — because it runs at preload/startup time.
Sanity check in CI. Add a job that generates the script and boots PHP with it in a container matching production, failing loudly on any fatal. A broken preload script takes down the entire FPM pool, so this check is cheap insurance.
A bad preload file is a hard outage, not a warning. Syntax errors, missing files, or unreadable paths cause PHP-FPM to fail to start — every worker, not one request. Always validate in staging first, and always keep the php.ini change as the easily-reverted switch.
Use absolute paths, and mind the user. opcache.preload needs an absolute path. If the FPM master runs as root, PHP refuses to preload without opcache.preload_user set to the pool user. The preload file must be readable by that user at startup.
Top-level code executes at preload time. Preloading is not "cache this file for later" — the file's top-level statements run once in the FPM master context. Files that echo, read env vars, register globals, open connections, or define conditionally-guarded classes can behave very differently here than in a request. Skip anything not purely declarative.
Ordering and duplicate declarations. Classes must be preloaded after their parents, interfaces, and traits. Two paths to the same file (symlinked release directories, realpath mismatches) can produce "Cannot declare class … name is already in use" at startup. Use consistent, resolved paths, and check that opcache.revalidate_path matches how your deploys are laid out.
Never preload churn. Compiled Blade views, generated proxies, per-deploy cache artifacts, and anything that changes frequently bloat shared memory and give zero benefit. Same for large chunks of vendor/ that only load on rare code paths — measure, don't guess.
CLI is not FPM. php artisan preload:generate runs under the CLI binary with the CLI php.ini. Different extensions, different ini values, different user. Always validate the generated file under FPM (a staging reload) rather than trusting the CLI run.
Preloaded scripts are immutable for the process lifetime. opcache_reset() will not swap them out. Every code change that touches a preloaded file requires an FPM reload. If you see "old code still running" after a deploy, this is almost always why.
Memory pressure is real. Preloading pulls everything into shared memory up front. Watch opcache_get_status()['preload_statistics']['memory_consumption'] against opcache.memory_consumption; if you start seeing cache-full restarts, either raise the limit or trim the list. There is a point of diminishing returns — preloading more than the hot path can make things slower to start and no faster to serve.
Keep tracing off outside local/staging. Discovery modes accumulate state per request; left enabled in production they are a memory leak and an information-disclosure surface.
Extension points worth knowing: the published config (file list, ignore rules, output path), the generation command (safe to wrap in your own deploy script), and the facade for appending entries at boot. Between those three you can cover nearly every case without forking.
On v4.x specifically: this is a new major. If you are coming from v3, read the changelog before upgrading — config keys, command names, and the shape of the generated script have changed between majors historically. Regenerate preload.php from scratch after upgrading rather than reusing the old artifact, and re-run your staging boot check, since a stale generated script combined with a new API is the most likely upgrade failure mode.
How can I help you explore Laravel packages today?