Новинка Виртуальный VDS сервер в Нидерландах от 390р
Выберите продукт

PHP OPcache на VDS: настройка, opcache_reset и deploy без старого кода

OPcache ускоряет PHP, но на VDS важно не просто включить расширение, а правильно выделить память, выбрать режим проверки файлов и встроить сброс кэша в deploy. В статье — рабочие настройки, команды и типовые ошибки.
PHP OPcache на VDS: настройка, opcache_reset и deploy без старого кода

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.

Проверка PHP-FPM и конфигурационных файлов OPcache

Где находятся настройки 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-контейнеры и библиотеки читают аннотации или документационные комментарии. Выключение может дать микроскопическую экономию, но риск поломки выше.

Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Как подобрать 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:

  1. загрузить новый релиз в отдельный каталог;
  2. установить зависимости без dev-пакетов;
  3. выполнить миграции с учётом совместимости старого и нового кода;
  4. переключить симлинк current атомарной командой;
  5. сбросить OPcache через reload PHP-FPM или защищённый endpoint;
  6. проверить healthcheck и ключевые страницы;
  7. оставить старый релиз для быстрого 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 исполняет старый или несуществующий файл.

Атомарный deploy PHP-приложения со сбросом OPcache

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 в порядок без долгой теории, пройдитесь по этому списку:

  1. Убедитесь, что OPcache загружен именно в PHP-FPM.
  2. Проверьте актуальный ini-файл для вашей версии PHP.
  3. Начните с opcache.memory_consumption=192 или 256 для фреймворка.
  4. Поставьте opcache.max_accelerated_files=20000 или выше для крупных проектов.
  5. Для production с CI/CD решите, как будете сбрасывать OPcache при deploy.
  6. Не выключайте opcache.validate_timestamps, пока deploy не стал повторяемым.
  7. Добавьте мониторинг memory_usage, opcache_statistics, oom_restarts и hit_rate.
  8. Документируйте команду reload для каждой версии PHP-FPM на сервере.

OPcache — простой инструмент, если относиться к нему как к части deploy-процесса, а не как к разовой галочке в php.ini. На VDS он помогает экономить CPU, снижает лишние обращения к диску и делает поведение PHP-приложения стабильнее. Главное — выделить достаточно памяти, понимать разницу между CLI и PHP-FPM, и всегда синхронизировать сброс кэша с выкладкой нового кода.

Поделиться статьей

Вам будет интересно

Linux quotas на VDS: user quota, project quota, /var/www и лимиты для бэкапов OpenAI Статья написана AI (GPT 5)

Linux quotas на VDS: user quota, project quota, /var/www и лимиты для бэкапов

Разбираем, как включить disk quota на VDS, ограничить пользователей, сайты и каталоги бэкапов. Покажем ext4 и XFS, user quota, pro ...
Nginx и Apache: gzip, Brotli и кэш сжатых ответов OpenAI Статья написана AI (GPT 5)

Nginx и Apache: gzip, Brotli и кэш сжатых ответов

Разберём, когда включать gzip и Brotli, какие файлы сжимать на лету или отдавать заранее, как не сломать Vary: Accept-Encoding и п ...
Linux VDS: что делать при nf_conntrack table full OpenAI Статья написана AI (GPT 5)

Linux VDS: что делать при nf_conntrack table full

Если на VDS внезапно рвутся SSH, API и соединения контейнеров, а в dmesg видно conntrack table full, проблема часто в таблице отсл ...