The realpath cache is one of those PHP internals that most developers never think about until performance profiling points at filesystem stat calls eating significant request time. PHP resolves file paths on every include, require, file_exists(), and autoloader lookup. The realpath cache stores resolved paths so PHP does not have to ask the filesystem repeatedly. When it overflows, PHP falls back to hitting the disk for every path resolution, and on applications with hundreds of included files per request, that adds up fast.
The default realpath_cache_size is 4 MB, which is sufficient for small applications. Legacy PHP applications with deep dependency trees, large vendor directories, and extensive autoloader maps routinely exceed this limit without anyone noticing. The result is invisible performance degradation: no errors, no warnings, just slower requests.
What the Realpath Cache Does
When PHP encounters a file path like vendor/laminas/laminas-servicemanager/src/ServiceManager.php, it needs to resolve that relative path to an absolute filesystem path. This involves:
- Resolving symlinks
- Normalizing directory separators
- Checking that each directory component in the path exists
- Resolving
.and..components
Each of these steps requires system calls to the filesystem. On a local SSD, each call is microseconds. On a network filesystem (NFS, EFS, GlusterFS), each call can take milliseconds. On a legacy application that includes 300 to 500 files per request, those microseconds or milliseconds compound.
The realpath cache maps each resolved path to its absolute location. Subsequent requests for the same path skip the filesystem entirely and return the cached result.
Measuring Your Current Usage
Do not tune the realpath cache by guessing. Measure it.
Check Current Settings
1 | echo 'realpath_cache_size: ' . ini_get('realpath_cache_size') . PHP_EOL; |
Check Current Usage
PHP provides realpath_cache_size() and realpath_cache_get() functions:
1 | // At the end of a typical request (in a shutdown function or debug endpoint) |
Interpret the Numbers
- Under 50% usage: Your cache is adequately sized. No changes needed.
- 50-80% usage: You have headroom but should monitor, especially during deployments when new paths are loaded.
- Over 80% usage: You are at risk of eviction. Increase the size.
- Over 95% usage: You are almost certainly experiencing evictions on every request. Fix this immediately.
Count Unique File Paths
For a more thorough assessment, dump all cached paths:
1 | $entries = realpath_cache_get(); |
Legacy applications often have 200 to 800 entries in the realpath cache. Each entry consumes memory proportional to the path length. Applications with deep directory structures or long vendor package names use more cache per entry.
Setting the Right Size
Calculate Based on Measurement
A simple formula:
1 | target_size = measured_usage * 1.5 |
If your application uses 3.2 MB of realpath cache at peak, set the size to 5 MB. The extra headroom accounts for deployment transients, code path variations, and growth.
Set in PHP Configuration
1 | ; php.ini or pool configuration |
For production servers with OPcache validate_timestamps=0 (which disables stat checks), you can set the TTL higher or even set it to 0 (never expire within a process lifetime) because the cache only needs to be populated once per worker process.
For PHP-FPM Pools
Each PHP-FPM worker maintains its own realpath cache. The realpath_cache_size value is per-worker, not shared. If you have 50 workers with an 8 MB cache, you are allocating up to 400 MB of total memory to realpath caching across all workers.
This is usually fine because the cache is populated lazily (only paths actually accessed are cached) and most workers will not reach the maximum. But on memory-constrained servers, factor this into your PHP-FPM memory planning.
The TTL Setting
realpath_cache_ttl determines how many seconds a cached path remains valid. After the TTL expires, PHP will re-verify the path against the filesystem on the next access.
Development
Set TTL low in development so file moves and renames are detected immediately:
1 | realpath_cache_ttl = 2 |
Production
Set TTL high in production because deployed code does not change between deployments:
1 | realpath_cache_ttl = 600 |
If you deploy by replacing the application directory (symlink deployment), the old worker processes will use the old paths until they are recycled. This is normal behaviour. After a PHP-FPM reload or graceful restart, new workers build fresh caches.
Interaction with OPcache
OPcache and the realpath cache serve different purposes but interact in important ways:
- OPcache caches compiled bytecode. It uses its own file tracking and does not depend on the realpath cache for bytecode lookup.
- The realpath cache is used by PHP’s include/require system, autoloaders, and file functions. OPcache’s file validation (when
validate_timestamps=1) uses separate stat calls.
When both are properly configured:
- The autoloader resolves a class name to a file path (realpath cache helps here)
- OPcache serves the compiled bytecode from shared memory (no file read needed)
- The result is fast class loading with minimal filesystem interaction
When the realpath cache is undersized:
- The autoloader triggers a filesystem stat for every include
- Even though OPcache has the bytecode cached, the stat call adds latency
- Under high concurrency, filesystem stat calls can become a bottleneck
The PHP Performance Playbook covers OPcache configuration in detail. Tuning both together produces the best result.
Monitoring in Production
Periodic Sampling
Add a monitoring script that runs periodically and reports cache usage:
1 | // monitoring/realpath-cache-check.php |
Post-Deployment Checks
After every deployment, check the realpath cache usage from a fresh worker. Deployments that add new dependencies or restructure directories can significantly change the cache footprint.
Common Mistakes
Setting the cache too large without measuring. Memory allocated to the realpath cache is memory unavailable for other purposes. A 64 MB realpath cache on an application that uses 2 MB is wasteful, especially when multiplied across 50 PHP-FPM workers.
Forgetting that the cache is per-worker. Setting realpath_cache_size=32M with 100 workers means up to 3.2 GB of total realpath cache memory. Measure actual usage and size accordingly.
Ignoring the TTL in development. A high TTL in development means renamed or moved files remain cached under their old paths until the TTL expires or the development server restarts. This causes confusing “file not found” errors.
Not reloading PHP-FPM after changing the setting. The realpath_cache_size is read at process start. Changing it in php.ini has no effect on running workers until they are restarted.
FAQ
Does the realpath cache matter if I use OPcache preloading?
Preloaded files bypass the autoloader and do not use the realpath cache for loading. However, files that are not preloaded still use it. If you preload your framework but not your application classes, the realpath cache still matters for application class autoloading.
Does the realpath cache work on Windows?
Yes, but the behaviour differs because Windows path resolution involves different system calls. The same measurement approach applies.
Can I disable the realpath cache?
Setting realpath_cache_size=0 disables it. This is almost never a good idea because it forces filesystem lookups on every include. The only scenario where disabling makes sense is debugging path resolution issues.
Next Steps
Measure your realpath cache usage on a production or staging server using the code above. If you are over 80%, increase the size. If you are under 50%, your current setting is fine.
For the broader performance picture, the PHP Performance Playbook covers the full stack of PHP performance tuning, and the Performance Optimisation chapter covers the application-level optimisation strategies that work alongside runtime tuning.
The PHP 8.5 Performance Reality Check covers how version upgrades interact with these runtime settings to produce measurable improvements.