PHP OPcache — один из тех компонентов, который обычно «просто включают» и забывают. До первого странного инцидента: после deploy пользователи видят старый код, один PHP-FPM pool работает быстрее другого, память VDS внезапно заканчивается, а команда opcache_reset() вроде выполнена, но ничего не изменила.
В этой статье разберём OPcache с позиции администратора VDS: где лежат настройки, сколько памяти выделять, как работать с opcache.validate_timestamps, почему CLI и PHP-FPM — это разные миры, и как безопасно сбрасывать кэш при выкладке.
Материал рассчитан на типичную связку Nginx или Apache с PHP-FPM, но сами принципы одинаковы для большинства PHP-приложений: WordPress, Laravel, Symfony, Yii, Bitrix, самописные панели и API. Важно понимать: OPcache ускоряет не бизнес-логику и не SQL-запросы, а этап компиляции PHP-файлов в байткод.
Что делает PHP OPcache и почему на VDS он особенно важен
Каждый PHP-запрос без OPcache проходит путь: прочитать PHP-файлы с диска, распарсить, скомпилировать в байткод, выполнить. Если приложение состоит из сотен или тысяч файлов, а автозагрузчик Composer подтягивает десятки классов на запрос, компиляция становится постоянным налогом на CPU и файловую систему.
PHP OPcache сохраняет уже скомпилированный байткод в разделяемой памяти процесса. Следующий запрос берёт готовый результат из памяти и быстрее переходит к выполнению. На VDS это особенно полезно: ресурсы ограничены тарифом, нагрузка может быть пиковая, а лишняя компиляция PHP-файлов конкурирует с базой данных, веб-сервером, очередями и cron-задачами.
OPcache не заменяет Redis, Memcached, FastCGI cache или CDN. Это кэш уровня PHP-движка: он помогает быстрее запускать код, но не кэширует HTML-страницы, SQL-результаты и API-ответы.
Если вам нужно кэшировать данные приложения, посмотрите отдельно материал про Redis, Memcached и кэширование в PHP-проектах. OPcache хорошо дополняет такие решения, но решает другую задачу.
На практике OPcache почти всегда стоит включать в production. Исключения редки: диагностика расширений, отладка нестандартных загрузчиков, временные тесты совместимости. Для PHP 7.4, 8.1, 8.2, 8.3 и 8.4 OPcache — нормальная часть production-конфигурации.
Установка и проверка OPcache
Во многих дистрибутивах OPcache ставится вместе с PHP, но полагаться на это не стоит. Сначала проверим, загружено ли расширение в CLI:
php -v
php -m | grep -i opcache
php --ini
Если в выводе есть Zend OPcache, расширение доступно для CLI. Но это ещё не гарантирует, что оно загружено именно в PHP-FPM, который обслуживает сайт. У CLI и FPM могут быть разные ini-файлы, разные версии PHP и даже разные наборы расширений.
Debian и Ubuntu
Для Debian и Ubuntu имя пакета обычно зависит от версии PHP:
sudo apt update
sudo apt install php8.3-fpm php8.3-opcache
sudo systemctl enable --now php8.3-fpm
sudo systemctl status php8.3-fpm
Если у вас другая версия PHP, замените 8.3 на актуальную: например, 8.2 или 8.4. Посмотреть доступные службы PHP-FPM можно так:
systemctl list-units 'php*-fpm.service' --all
AlmaLinux, Rocky Linux, CentOS Stream, Oracle Linux и Fedora
В RHEL-based системах и Fedora чаще используются общие имена пакетов:
sudo dnf install php-fpm php-opcache
sudo systemctl enable --now php-fpm
sudo systemctl status php-fpm
Если PHP установлен из стороннего репозитория или через модульные потоки, проверьте, что пакет php-opcache относится к той же версии PHP, что и php-fpm. Смешивание пакетов разных источников — частая причина ситуации «в CLI OPcache есть, а сайт его не видит».
FreeBSD
На FreeBSD имена пакетов также привязаны к версии PHP:
sudo pkg install php83 php83-opcache
sudo sysrc php_fpm_enable=YES
sudo service php_fpm start
php -m | grep -i opcache
Конфигурация PHP на FreeBSD обычно находится в /usr/local/etc/php. После изменения ini-файлов перезапускайте или перезагружайте php_fpm.

Где находятся настройки OPcache
OPcache настраивается через ini-файлы PHP. Точный путь зависит от ОС, версии PHP и способа установки. Типичные варианты:
- Debian/Ubuntu:
/etc/php/8.3/fpm/conf.d/10-opcache.iniи/etc/php/8.3/cli/conf.d/10-opcache.ini; - RHEL-based и Fedora:
/etc/php.d/10-opcache.ini; - FreeBSD:
/usr/local/etc/php/ext-10-opcache.iniили файлы в/usr/local/etc/php.
Для сайта важна конфигурация FPM, а не только CLI. Поэтому после изменения параметров обязательно перезагрузите PHP-FPM. Проверять только php -i недостаточно: эта команда показывает настройки CLI SAPI.
Чтобы не гадать, можно временно создать диагностический PHP-файл с phpinfo() в закрытом тестовом каталоге, открыть его с вашего IP, проверить блок OPcache и сразу удалить файл. Не оставляйте phpinfo() в публичном доступе: он раскрывает пути, версии, переменные окружения и часть конфигурации сервера.
Базовая production-конфигурация OPcache
Ниже — стартовый набор параметров для небольшого или среднего проекта на VDS. Его нельзя считать универсальной «магической формулой», но это хороший базис, от которого удобно отталкиваться.
opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=192
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1
opcache.revalidate_freq=2
opcache.save_comments=1
opcache.enable включает OPcache для веб-запросов. Для production почти всегда должно быть 1.
opcache.enable_cli управляет OPcache в CLI. Обычно на сервере с deploy, cron и миграциями его оставляют 0, чтобы консольные команды не путались со своим отдельным OPcache. Включать CLI-кэш имеет смысл для долгоживущих CLI-процессов или специфичных задач.
opcache.memory_consumption — размер разделяемой памяти под байткод, в мегабайтах. Для маленького WordPress может хватить 96–128 МБ, для Laravel или Symfony с большим vendor-каталогом лучше начинать со 192–256 МБ. Для нескольких сайтов в одном PHP-FPM master-процессе память общая, поэтому считать нужно суммарно.
opcache.interned_strings_buffer — память под интернированные строки: имена классов, методов, путей, повторяющиеся строковые значения. Если проект большой и использует Composer, 16 МБ — хороший старт. Если видите заполнение буфера по метрикам, поднимайте до 24 или 32 МБ.
opcache.max_accelerated_files задаёт максимальное число PHP-файлов, которые OPcache может хранить. Значение 10000 может быть мало для современных фреймворков с vendor-зависимостями. Для одного крупного проекта обычно ставят 20000 или 40000. Для нескольких сайтов на одном VDS — ещё выше, но без фанатизма: слишком большое значение не заменяет нормальный учёт памяти.
opcache.save_comments лучше оставлять включённым, потому что некоторые фреймворки, ORM, DI-контейнеры и библиотеки читают аннотации или документационные комментарии. Выключение может дать микроскопическую экономию, но риск поломки выше.
Как подобрать opcache.memory_consumption
Главный симптом нехватки opcache.memory_consumption — заполнение кэша и рост числа рестартов OPcache. Когда памяти недостаточно, новые скрипты перестают кэшироваться или кэш сбрасывается чаще, чем нужно. В итоге вы думаете, что OPcache включён, но часть запросов снова компилирует файлы.
Для грубой оценки можно ориентироваться на тип проекта:
- небольшой PHP-сайт или лендинг: 64–128 МБ;
- WordPress с умеренным числом плагинов: 128–192 МБ;
- Laravel, Symfony, Yii, Drupal, Bitrix: 192–384 МБ;
- несколько сайтов в одном FPM: 256–512 МБ и обязательный мониторинг.
Но лучше смотреть фактическое состояние. Для этого используют opcache_get_status(). Его нужно выполнять внутри того же SAPI, который обслуживает сайт, то есть через PHP-FPM, а не обычным php в консоли.
<?php
$status = opcache_get_status(false);
header('Content-Type: text/plain');
print_r($status['memory_usage']);
print_r($status['opcache_statistics']);
Такой файл должен быть временным и закрытым от внешних пользователей. После проверки удалите его. Если автоматизируете мониторинг, лучше отдавать только нужные числовые показатели и ограничивать доступ по IP на уровне веб-сервера.
Смотрите прежде всего на свободную память OPcache, количество cached scripts, misses, oom_restarts и hash_restarts. Если oom_restarts растёт — увеличивайте opcache.memory_consumption. Если max_cached_keys близко к лимиту — увеличивайте opcache.max_accelerated_files.
opcache.validate_timestamps: удобство против предсказуемости
Параметр opcache.validate_timestamps отвечает за проверку изменения PHP-файлов на диске. При значении 1 OPcache периодически проверяет timestamp файла и при изменении перекомпилирует байткод. Частота проверки задаётся параметром opcache.revalidate_freq в секундах.
Для разработки и небольших сайтов это удобно: загрузили новый файл — через пару секунд PHP-FPM увидит изменения. Но в production у этого режима есть минусы: лишние проверки файловой системы, неполная предсказуемость во время deploy и окно, когда часть воркеров может работать со старым байткодом.
При opcache.validate_timestamps=0 PHP не проверяет изменения файлов автоматически. Это быстрее и предсказуемее, но после выкладки кода вы обязаны явно сбросить OPcache или перезагрузить PHP-FPM. Иначе сайт продолжит выполнять старый байткод, хотя файлы на диске уже новые.
Практичный подход такой:
- для dev/stage:
opcache.validate_timestamps=1иopcache.revalidate_freq=0или1; - для небольшого production без строгого deploy-процесса:
opcache.validate_timestamps=1иopcache.revalidate_freq=2; - для production с CI/CD и атомарными релизами:
opcache.validate_timestamps=0и явный сброс при deploy.
Если вы только настраиваете сервер и ещё не уверены в deploy-процессе, не спешите выключать проверку timestamp. Сначала добейтесь повторяемой выкладки, логирования и понятного rollback. Потом переводите OPcache в строгий режим.
PHP-FPM, несколько пулов и отдельные версии PHP
На VDS часто живёт несколько сайтов: один на PHP 8.2, другой на PHP 8.3, третий в отдельном FPM pool с другим пользователем. Важно помнить: OPcache разделяется в рамках master-процесса PHP-FPM конкретной версии. Если у вас два независимых сервиса php8.2-fpm и php8.3-fpm, у них разные OPcache.
Если у вас один php-fpm и несколько pools, кэш обычно общий для этого master-процесса. Это удобно по памяти, но создаёт нюанс: сброс OPcache затрагивает все сайты, обслуживаемые этим FPM. Для маленького VDS это нормально, а для многопользовательского сервера может быть нежелательно.
При изоляции проектов учитывайте не только pm.max_children, но и OPcache. Если один большой проект заполняет opcache.max_accelerated_files, соседние сайты могут получать меньше пользы от кэша. В такой ситуации помогают отдельные версии PHP-FPM, отдельные контейнеры или аккуратное увеличение лимитов.
opcache_reset(): почему команда из консоли часто не помогает
Функция opcache_reset() сбрасывает OPcache текущего PHP-процесса. И здесь многие попадаются: если выполнить её через php reset.php в консоли, вы сбросите CLI OPcache, а не кэш PHP-FPM, который обслуживает веб-запросы. Если opcache.enable_cli=0, команда вообще почти ничего полезного не сделает.
Чтобы сбросить OPcache веб-приложения, вызов должен пройти через PHP-FPM. Есть три распространённых способа:
- graceful reload PHP-FPM после deploy;
- временный или защищённый HTTP-endpoint, доступный только локально или из CI/CD-сети;
- интеграция сброса в приложение или deploy hook, если фреймворк это поддерживает.
Самый простой и надёжный вариант для большинства VDS — reload PHP-FPM. Он не требует держать публичный reset-файл и хорошо вписывается в systemd.
Перезагрузка PHP-FPM после deploy
Debian/Ubuntu:
sudo systemctl reload php8.3-fpm
sudo systemctl status php8.3-fpm
RHEL-based и Fedora:
sudo systemctl reload php-fpm
sudo systemctl status php-fpm
FreeBSD:
sudo service php_fpm reload
sudo service php_fpm status
Если reload не поддерживается конкретной сборкой или ведёт себя нестабильно, используйте restart, но учитывайте кратковременное влияние на активные запросы:
sudo systemctl restart php8.3-fpm
Для production лучше проверить поведение заранее на staging: как приложение реагирует на reload, не теряются ли долгие запросы, корректно ли работают очереди и cron.
Защищённый endpoint для opcache_reset()
Иногда reload всего PHP-FPM нежелателен: например, на одном FPM сидит несколько проектов, а сброс нужен только после deploy одного сайта. Тогда можно использовать endpoint, который вызывается через веб-сервер и выполняется внутри нужного pool. Он должен быть закрыт максимально строго.
<?php
if (($_SERVER['REMOTE_ADDR'] ?? '') !== '127.0.0.1') {
http_response_code(403);
exit('forbidden');
}
if (!function_exists('opcache_reset')) {
http_response_code(500);
exit('opcache_reset is unavailable');
}
$result = opcache_reset();
header('Content-Type: text/plain');
echo $result ? 'ok' . PHP_EOL : 'failed' . PHP_EOL;
Такой файл не стоит хранить в репозитории как обычную часть приложения. Лучше создавать его на время deploy, вызывать локально и удалять. Если endpoint постоянный, ограничьте доступ на уровне Nginx или Apache, добавьте отдельный секрет, логируйте вызовы и убедитесь, что файл не доступен извне.
Пример локального вызова после выкладки:
curl --fail --silent --show-error http://127.0.0.1/opcache-reset.php
В реальной конфигурации endpoint лучше разместить не в публичном маршруте приложения, а в отдельном location, доступном только с loopback-интерфейса или из закрытой deploy-сети.
Deploy без старого кода: рабочая схема
OPcache особенно важен при атомарном deploy через директории релизов и симлинк current. Типичная структура выглядит так: CI загружает новый релиз в отдельную папку, выполняет composer install, прогревает кэши фреймворка, затем переключает симлинк current на новую версию. Это удобно для rollback, но OPcache может продолжать держать байткод файлов из старого релиза.
Если у вас opcache.validate_timestamps=1, проблема обычно менее заметна, но всё равно возможны короткие расхождения между воркерами. Если opcache.validate_timestamps=0, без явного сброса старый код может жить до перезапуска FPM.
Минимальная последовательность deploy для production:
- загрузить новый релиз в отдельный каталог;
- установить зависимости без dev-пакетов;
- выполнить миграции с учётом совместимости старого и нового кода;
- переключить симлинк
currentатомарной командой; - сбросить OPcache через reload PHP-FPM или защищённый endpoint;
- проверить healthcheck и ключевые страницы;
- оставить старый релиз для быстрого rollback.
Пример переключения симлинка:
ln -sfn /var/www/app/releases/2026-02-10-120000 /var/www/app/current
sudo systemctl reload php8.3-fpm
Если вы используете Nginx, убедитесь, что root или alias указывает на current/public, а PHP-FPM получает корректный SCRIPT_FILENAME. Ошибки в путях могут привести к тому, что веб-сервер видит новый релиз, а PHP-FPM исполняет старый или несуществующий файл.

realpath_cache и OPcache: соседний источник сюрпризов
При symlink-based deploy стоит помнить не только про OPcache, но и про кэш реальных путей PHP — realpath_cache. Он не является частью OPcache, но влияет на то, как PHP разрешает пути через симлинки. Если после переключения current приложение странно ищет файлы в старом релизе, проверьте параметры realpath_cache_size и realpath_cache_ttl.
Для production часто ставят умеренный TTL, например:
realpath_cache_size=4096K
realpath_cache_ttl=120
Если у вас строгий deploy с opcache.validate_timestamps=0, reload PHP-FPM после переключения релиза одновременно решает и вопрос OPcache, и вопрос долгоживущего realpath cache. Поэтому reload остаётся самым понятным способом синхронизировать состояние PHP с новым кодом.
Чего не стоит делать
Есть несколько ошибок, которые встречаются чаще всего:
- сбрасывать OPcache командой из CLI и ожидать, что очистится PHP-FPM;
- ставить
opcache.validate_timestamps=0без deploy hook для сброса кэша; - держать публичный
opcache_reset-файл без ограничения доступа; - выделять слишком мало памяти в
opcache.memory_consumptionи не смотреть наoom_restarts; - менять ini-файлы и забывать reload PHP-FPM;
- одинаково настраивать dev, stage и production, хотя требования к ним разные.
Отдельно стоит сказать про JIT. В PHP 8 JIT может помогать в вычислительных задачах, но для типичного веб-приложения с базой данных, шаблонами и сетью он редко даёт серьёзный выигрыш. Не начинайте оптимизацию VDS с JIT. Сначала настройте OPcache, PHP-FPM, базу данных, файловый кэш и наблюдаемость.
Быстрый чек-лист для VDS
Если нужно привести OPcache в порядок без долгой теории, пройдитесь по этому списку:
- Убедитесь, что OPcache загружен именно в PHP-FPM.
- Проверьте актуальный ini-файл для вашей версии PHP.
- Начните с
opcache.memory_consumption=192или256для фреймворка. - Поставьте
opcache.max_accelerated_files=20000или выше для крупных проектов. - Для production с CI/CD решите, как будете сбрасывать OPcache при deploy.
- Не выключайте
opcache.validate_timestamps, пока deploy не стал повторяемым. - Добавьте мониторинг
memory_usage,opcache_statistics,oom_restartsиhit_rate. - Документируйте команду reload для каждой версии PHP-FPM на сервере.
OPcache — простой инструмент, если относиться к нему как к части deploy-процесса, а не как к разовой галочке в php.ini. На VDS он помогает экономить CPU, снижает лишние обращения к диску и делает поведение PHP-приложения стабильнее. Главное — выделить достаточно памяти, понимать разницу между CLI и PHP-FPM, и всегда синхронизировать сброс кэша с выкладкой нового кода.


