Quick Tip: PHP FPM requires a restart after nearly every change without this setting
Ever set up a new staging or remote dev server for PHP only to find yourself needing to restart PHP FPM after nearly every change just to get them to take? I can show you two ways to solve this.
Let's cut to the chase: if you're tired of restarting PHP FPM after every code change on your server, you basically have two options to make changes show up without a restart:
Disable OPcache entirely by setting
opcache.enable = 0in your php.ini - I don't recommend this; see why below.Enable automatic file checking by setting
opcache.validate_timestamps = 1(usually in something like/etc/php/8.4/mods-available/opcache.inion Ubuntu/Debian setups).
To get to why both of these options work we have to dig into a little how OPcache works. PHP is an interpreted language, so before running your code on each request, there's a compile step. The compile step converts your normal PHP code into low-level instructions called opcodes. The problem is sometimes that compile step can take a long time - upwards of 100-300ms. That's just for compiling, not executing the logic.
OPcache solves this by caching those compiled opcodes in shared memory. The first time a script runs, PHP compiles it and stores the opcodes. On future requests, it skips recompiling and reuses the cached version. If OPcache is fully enabled and optimized for production (like on Laravel Forge servers), it often has opcache.validate_timestamps = 0. In that mode, OPcache never checks the filesystem for updates after caching a file. The cached opcodes live forever in memory until you restart/reload PHP FPM (which spins up fresh worker processes that recompile from disk), call opcache_reset(), or invalidate specific files.
That is why you need to restart PHP FPM after every change; the workers are happily serving stale code.
So how does opcache.validate_timestamps = 1 change things? When enabled, OPcache attaches the file's modification timestamp to each cached script entry. Then, whenever PHP needs that script:
It looks for a cached version.
If found, and enough time has passed since the last check (controlled by
opcache.revalidate_freq), it compares the file's current mtime on disk against the one stored in the cache.If the disk file is newer => recompile, update the cache entry with fresh opcodes and the new timestamp.
If unchanged => reuse the cache.
The opcache.revalidate_freq setting throttles these checks so OPcache doesn't hammer the filesystem on every request. It tells PHP how often to check the filesystem for changes to the source code. The default is 2 seconds, which strikes a nice balance — changes usually appear within 0–2 seconds on the next request, but you avoid constant overhead on busy servers. The revalidation frequency value is there as a failsafe to make sure that at some point the OPcache will be fully rotated and new changes to the source code are compiled.
My personal recommendation is the following:
Always enable OPcache -
opcache.enable = 1On production servers keep
opcache.validate_timestamps = 0(Laravel Forge defaults to this when optimized) and rely on your deployment script to reload PHP FPMOn staging/dev/remote servers where you're editing files directly via SSH/FTP and there is no auto-deploy script exists - set
opcache.validate_timestamps = 1so changes can be recompiled quickly without restarts