opcache.revalidate_freq is why the deploy served old code

A deploy finishes, the files on disk are correct, and the site serves the previous release for another minute. OPcache is not checking whether a file changed on every request — revalidate_freq is how many seconds it waits before it will stat a given file again, and until then the cached opcodes are served regardless.

# php.ini — one stat per file per minute, so up to a minute of stale code
opcache.validate_timestamps=1
opcache.revalidate_freq=60

# production, once the deploy is responsible for the reload
opcache.validate_timestamps=0

# this does not clear the pool's cache: the CLI has its own, or none at all
php -r 'opcache_reset();'

# this does
kill -USR2 "$(cat /var/run/php-fpm.pid)"

The setting people reach for first is revalidate_freq=0, which stats every file on every request — correct, and it gives back a measurable slice of what OPcache was installed to save. The better arrangement is validate_timestamps=0, which never checks at all, plus a reload in the deploy script. Two traps come with that. opcache_reset() run from the CLI does nothing to a php-fpm pool, because they are separate processes with separate shared memory — it has to be called from a request served by that pool, or the pool reloaded outright. And if releases are deployed into dated directories behind a symlink, OPcache keys entries by resolved path, so a swap gives every file a new key: correct behaviour without any reload, at the price of a cold cache and the old release’s entries occupying memory until they are evicted.