OPcache and JIT

OPcache works out of the box with OxPHP. All PHP worker threads share a single OPcache memory segment. Scripts are compiled once at first execution and served from the cache by every worker thereafter. No special setup is required to enable this sharing.

How OPcache works with OxPHP

OxPHP registers itself as a named SAPI, and OPcache treats it identically to other server SAPIs. In practice:

  • All PHP worker threads use the same compiled opcode cache. When one worker compiles a file, every worker uses the result.
  • Scripts are not compiled per request. After the first request for each script, subsequent requests skip the parse and compile step entirely.
  • opcache.enable_cli does not affect OxPHP. That setting applies only to SAPIs named cli and phpdbg. OxPHP registers with the SAPI name cli-server, so OPcache is controlled solely via opcache.enable. The opcache.enable_cli setting is useful if you run PHP CLI in the same container (e.g., for migrations or Artisan commands). The official OxPHP image ships PHP CLI alongside the server binary, so you can set opcache.enable_cli=1 if your CLI scripts benefit from caching.

To enable OPcache, at minimum:

ini
[opcache] opcache.enable=1
Note

The official OxPHP Docker image is based on php:*-zts-alpine, which compiles OPcache statically into the PHP binary. Do NOT add zend_extension=opcache to your INI file. The extension is already loaded, and adding that line will cause a warning on every PHP startup. Only the [opcache] configuration section is needed.

These settings are optimized for production container deployments where PHP files do not change at runtime. Disable timestamp validation for maximum throughput. To also compile files at startup, add the two settings from Preloading.

ini
[opcache] opcache.enable=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=16 opcache.max_accelerated_files=10000 opcache.validate_timestamps=0 opcache.revalidate_freq=0 opcache.file_update_protection=0 opcache.jit_buffer_size=64M opcache.jit=tracing
Setting Recommended Value Description
memory_consumption 128 Shared memory in MB for compiled scripts. Increase if opcache_get_status() shows low free memory.
interned_strings_buffer 16 Memory in MB for interned strings shared across all workers.
max_accelerated_files 10000 Maximum number of cached scripts. Set this higher than your total .php file count.
validate_timestamps 0 When 0, OPcache never checks the filesystem for changes. Restart the container or call opcache_reset() to pick up code changes.
revalidate_freq 0 Seconds between filesystem checks. Has no effect when validate_timestamps=0.
file_update_protection 0 Seconds after file modification before the file is eligible for caching. Set to 0 to cache immediately at startup.

Development settings

For development, enable timestamp validation so code changes take effect without restarting the container. Disable JIT to get clearer stack traces during debugging.

ini
[opcache] opcache.enable=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=16 opcache.max_accelerated_files=10000 opcache.validate_timestamps=1 opcache.revalidate_freq=2 opcache.jit_buffer_size=0 opcache.jit=disable

With validate_timestamps=1, OPcache checks file modification times every revalidate_freq seconds. This adds a small per-request overhead but lets you edit PHP files and see changes on the first request after that interval.

This is the recommended development-mode reload strategy for OxPHP. OPcache checks an included file's timestamp at most once per revalidate_freq seconds, so code edits are picked up once that window has passed, without a container restart or an external file-watcher daemon. Use revalidate_freq=0 for immediate stat-per-include (highest accuracy, slightly more I/O), or revalidate_freq=2 to amortize the stat cost. The default shown above is a good balance, especially if your DOCUMENT_ROOT is on a slow bind-mount (Docker on macOS/Windows).

What is NOT reloaded by validate_timestamps

A few classes of changes still require a container restart (or worker recycle) even with validate_timestamps=1:

  • Preloaded files (opcache.preload) are linked into the server at startup and are never revalidated. After editing a preloaded file, restart the container.
  • In Worker Mode, bootstrap state (the autoloader, DI container, and any objects built in the outer scope) lives in worker memory. OPcache will recompile changed class files, but the worker will not re-run its bootstrap. For dev worker loops, call Worker::scheduleExit() at the end of each request (e.g. behind an OXPHP_DEV env flag) to recycle the worker, which re-executes the outer scope and picks up all changes.
  • Framework-level caches (the compiled Symfony container, Laravel route/config/view cache, Composer optimized classmap) are .php files OPcache revalidates, but the values inside them reference stale class paths or container IDs. Run the framework's cache:clear command; OPcache alone is not enough.
  • Non-PHP files include .env, composer.json, YAML/JSON config, and template files compiled outside OPcache. OPcache tracks only files it compiled; everything else needs a restart.

JIT compilation

OPcache's JIT compiler translates PHP opcodes into native machine code at runtime. Use tracing mode for the best optimization:

ini
opcache.jit=tracing opcache.jit_buffer_size=64M

JIT provides the most benefit for CPU-bound PHP code: math-heavy loops, string processing, image manipulation, and template rendering. For I/O-bound applications that spend most of their time waiting on database queries or external API calls, the improvement is minimal.

To disable JIT:

ini
opcache.jit=disable opcache.jit_buffer_size=0

Preloading

OPcache preloading compiles and caches PHP files at server startup, before any requests are handled. This eliminates first-request compilation cost entirely and makes classes and functions available globally without any require or autoload overhead.

Configure preloading in your INI file:

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

Create a preload.php script that loads your most frequently used files:

preload.php
<?php // preload.php — runs once at server startup require __DIR__ . '/vendor/autoload.php'; // Preload framework core files $files = glob(__DIR__ . '/vendor/symfony/http-kernel/**.php'); foreach ($files as $file) { opcache_compile_file($file); } // Preload hot application paths opcache_compile_file(__DIR__ . '/src/Controller/ApiController.php'); opcache_compile_file(__DIR__ . '/src/Service/UserService.php');
Note

Preloaded classes and functions are permanently available to all requests. They cannot be changed without restarting the server.

Worker Mode and preloading

If you use Worker Mode, your application is already initialized once: the autoloader, configuration, and database connections persist between requests. OPcache preloading complements this by eliminating opcode compilation overhead, but it does not replace application initialization. The two mechanisms work independently and can be used together.

Applying the PHP configuration

OxPHP reads PHP configuration from the standard conf.d directory. Use a Docker volume or a COPY instruction to supply your custom INI file.

bash
docker run -p 80:80 \ -v ./custom.ini:/usr/local/etc/php/conf.d/custom.ini:ro \ ghcr.io/oxphp/oxphp:0.13.0

Monitoring cache status

Inspect the live OPcache status from PHP to verify it is working:

php
<?php $status = opcache_get_status(); echo "Cached scripts: " . $status['opcache_statistics']['num_cached_scripts'] . "\n"; echo "Cache hits: " . $status['opcache_statistics']['hits'] . "\n"; echo "Cache misses: " . $status['opcache_statistics']['misses'] . "\n"; echo "Free memory: " . $status['memory_usage']['free_memory'] . " bytes\n";

If free_memory is consistently low, increase opcache.memory_consumption.

Prompts for your agent

These prompts assume an agent with the OxPHP documentation MCP server connected. The agent reads the pages it needs itself.

prompt
Tune OPcache for this repository's actual code size and how its image gets built. Read the `php/opcache` page through the OxPHP documentation MCP server. Count the `.php` files this project ships to size `max_accelerated_files` and `memory_consumption`, and pick `validate_timestamps` based on whether my Dockerfile bakes the code in or a bind mount overlays it at runtime.

See also

Found a mistake? Report it →