ZIM-НИЙ SAAALEЗимние скидки: до −50% на старт и −20% на продление
до 31.01.2026 Подробнее
Выберите продукт

Несколько версий PHP на одном VDS: Nginx + несколько PHP‑FPM пулов и изоляция сайтов

Нужно держать на одном сервере проекты на PHP 8.1, 8.2 и 8.3 одновременно и при этом изолировать сайты? Разбираем конфигурацию Nginx с несколькими пулами PHP‑FPM, vhost‑блоки, права, сокеты, типичные ошибки и контроль ресурсов на Ubuntu/Debian.
Несколько версий PHP на одном VDS: Nginx + несколько PHP‑FPM пулов и изоляция сайтов

Сценарий «несколько версий PHP на одном сервере» давно стал нормой: часть продакшена живет на стабильной ветке, свежие проекты требуют новейшую, а легаси — осторожно мигрируется. На Nginx это элегантно решается несколькими пулами php-fpm и привязкой каждого сайта к нужной версии и собственному пользователю. Всё это удобно поднимать на VDS: гибкая конфигурация, выделенные ресурсы и изоляция.

Архитектура: что именно мы настраиваем

Базовая идея проста: для каждой версии PHP создаются один или несколько пулов php-fpm. Каждый пул работает под своим системным пользователем и слушает отдельный Unix‑сокет. В конфигурации Nginx каждый виртуальный хост (vhost) направляет запросы .php на свой сокет и, соответственно, на нужный пул и версию PHP. Главные преимущества такого подхода:

  • Изоляция: процессы PHP одного сайта не получают прав другого, файловую систему разделяют UNIX‑права.
  • Гибкость: разные версии PHP под разные проекты без конфликтов.
  • Точечный тюнинг: параметры php.ini и FPM (pm, лимиты) индивидуально для каждого пула.
  • Надежность: перезагрузка пула не трогает остальные сайты, а opcache изолирован по пулам и версиям.

Золотое правило: изоляцию обеспечивает не «директива в конфиге», а системные пользователи и корректные права на файлы. Все остальное лишь усиливает модель.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Среда: Ubuntu / Debian, пакеты и версия PHP

На Ubuntu/Debian одновременно можно держать несколько веток PHP: например 8.1, 8.2 и 8.3. Если нужной версии нет в стандартном репозитории вашей системы, используйте backports или специализированный репозиторий для PHP. В остальном процедура одинакова: ставим пакеты phpX.Y-fpm, phpX.Y-cli и требуемые расширения для каждого проекта.

sudo apt update
sudo apt install php8.1-fpm php8.1-cli php8.1-mysql php8.1-xml php8.1-curl php8.1-zip php8.1-gd php8.1-mbstring php8.1-intl
sudo apt install php8.2-fpm php8.2-cli php8.2-mysql php8.2-xml php8.2-curl php8.2-zip php8.2-gd php8.2-mbstring php8.2-intl
sudo apt install php8.3-fpm php8.3-cli php8.3-mysql php8.3-xml php8.3-curl php8.3-zip php8.3-gd php8.3-mbstring php8.3-intl

Убедитесь, что служба FPM для каждой версии поднята:

systemctl status php8.1-fpm
systemctl status php8.2-fpm
systemctl status php8.3-fpm

Если предпочитаете панели, посмотрите сравнение актуальных решений: vds-panels-2025-comparison.

Пример: vhost Nginx с привязкой к сокету PHP‑FPM

Структура каталогов и пользователи для изоляции

Минимально достаточно выделить по системному пользователю на сайт и ограничить права на файлы. Пусть будут два проекта: site1 на PHP 8.3 и site2 на PHP 8.1.

sudo adduser --disabled-password --gecos "" site1
sudo adduser --disabled-password --gecos "" site2
sudo mkdir -p /var/www/site1/public
sudo mkdir -p /var/www/site2/public
sudo chown -R site1:site1 /var/www/site1
sudo chown -R site2:site2 /var/www/site2
sudo chmod -R o-rwx /var/www/site1 /var/www/site2

Папку public используем как document_root для Nginx, всё остальное — вне веб‑корня. Для большей безопасности вынесите секреты в файлы с правами 640, владельцем — пользователь сайта, группой — веб‑сервер (например, www-data), и запретите чтение остальным.

Пулы PHP‑FPM: один сайт — один пул

Создадим пул для site1 на PHP 8.3. Файл /etc/php/8.3/fpm/pool.d/site1.conf:

[site1]
user = site1
group = site1
listen = /run/php/php8.3-fpm-site1.sock
listen.owner = site1
listen.group = www-data
listen.mode = 0660
pm = ondemand
pm.max_children = 8
pm.process_idle_timeout = 20s
request_terminate_timeout = 300s
catch_workers_output = yes
php_admin_value[open_basedir] = /var/www/site1:/tmp
php_admin_value[upload_max_filesize] = 64M
php_admin_value[post_max_size] = 64M
php_admin_value[memory_limit] = 256M
php_admin_value[max_execution_time] = 300
php_admin_flag[log_errors] = on
php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,proc_get_status,pcntl_alarm,pcntl_fork,pcntl_waitpid
slowlog = /var/log/php8.3-fpm-site1.slow.log
request_slowlog_timeout = 5s
pm.status_path = /fpm-status
ping.path = /fpm-ping

И пул для site2 на PHP 8.1 — файл /etc/php/8.1/fpm/pool.d/site2.conf:

[site2]
user = site2
group = site2
listen = /run/php/php8.1-fpm-site2.sock
listen.owner = site2
listen.group = www-data
listen.mode = 0660
pm = ondemand
pm.max_children = 6
pm.process_idle_timeout = 20s
request_terminate_timeout = 300s
catch_workers_output = yes
php_admin_value[open_basedir] = /var/www/site2:/tmp
php_admin_value[upload_max_filesize] = 32M
php_admin_value[post_max_size] = 32M
php_admin_value[memory_limit] = 192M
php_admin_value[max_execution_time] = 180
php_admin_flag[log_errors] = on
php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,proc_get_status,pcntl_alarm,pcntl_fork,pcntl_waitpid
slowlog = /var/log/php8.1-fpm-site2.slow.log
request_slowlog_timeout = 5s
pm.status_path = /fpm-status
ping.path = /fpm-ping

Перезапускаем оба демона, чтобы появились сокеты в /run/php:

sudo systemctl reload php8.3-fpm
sudo systemctl reload php8.1-fpm

Если директория /run/php отсутствует после перезагрузки сервера, это означает, что она не была создана службой. Как правило, пакеты PHP‑FPM в Debian/Ubuntu создают её сами. Если требуется кастомный путь, убедитесь, что он существует и доступен Nginx.

Конфигурация пула PHP‑FPM для изоляции сайта

Nginx: два vhost‑блока, два сокета

Добавим два сайта, каждый на своём сокете. Пример для /etc/nginx/sites-available/site1.conf (ссылка в sites-enabled делается отдельно):

server {
    server_name site1.example;
    root /var/www/site1/public;
    index index.php index.html;

    access_log /var/log/nginx/site1.access.log;
    error_log  /var/log/nginx/site1.error.log;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $document_root;
        fastcgi_read_timeout 300s;
        try_files $uri =404;
        fastcgi_pass unix:/run/php/php8.3-fpm-site1.sock;
    }

    client_max_body_size 64m;
}

И /etc/nginx/sites-available/site2.conf:

server {
    server_name site2.example;
    root /var/www/site2/public;
    index index.php index.html;

    access_log /var/log/nginx/site2.access.log;
    error_log  /var/log/nginx/site2.error.log;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $document_root;
        fastcgi_read_timeout 300s;
        try_files $uri =404;
        fastcgi_pass unix:/run/php/php8.1-fpm-site2.sock;
    }

    client_max_body_size 32m;
}

Обратите внимание на явный fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name — это снимает проблему «No input file specified.» и позволяет гибко управлять корнем сайта. Привязка к Unix‑сокетам повышает безопасность и обычно даёт меньшие накладные расходы, чем TCP на 127.0.0.1:900x.

CLI‑версии PHP и composer для каждого проекта

Для CLI удобно вызывать конкретную версию напрямую: /usr/bin/php8.3, /usr/bin/php8.1. Так cron‑задачи и деплой‑скрипты всегда исполняются нужной версией.

php8.3 -v
php8.1 -v
crontab -e
# Пример: */5 * * * * cd /var/www/site1 && /usr/bin/php8.3 artisan schedule:run --no-interaction

Если требуется переключать «дефолтный» php для интерактивной сессии, используйте update-alternatives:

sudo update-alternatives --config php
php -v

Тюнинг производительности и памяти

pm = ondemand хорош на VDS со средними нагрузками и пиковыми всплесками: процессы появляются по запросу и завершаются после простоя. Для стабильного потока можно рассмотреть pm = dynamic и настроить pm.max_children, pm.start_servers, pm.min_spare_servers, pm.max_spare_servers. Грубая оценка памяти: сложите RSS одного воркера (с учётом расширений и opcache) и умножьте на pm.max_children; добавьте запас под веб‑сервер и БД. Не забывайте, что opcache выделяется отдельно в php.ini/php_admin_value и существует на уровне процесса пула, то есть не делится между пулами и версиями.

Для горячих CMS ускоряет стабильность включённый opcache.validate_timestamps = 1 с разумным revalidate_freq. Если вы делаете atomic‑деплой, можно оставить validate_timestamps = 0 и исполнять reload пула для инвалидации кэша после релиза. По теме кэширования и компрессии на шаред‑площадках смотрите: shared-hosting-php-opcache-brotli.

Безопасность: изоляция в деталях

  • Разделение пользователей: каждый пул работает под своим uid/gid. Это ключ к файловой изоляции.
  • Права на сокеты: listen.owner, listen.group, listen.mode = 0660. Группа веб‑сервера должна иметь доступ к сокету, но не ко всем файлам сайта.
  • Файловые права: закрываем «other», минимум прав группе. Секреты храним вне document_root.
  • open_basedir: дополнительная страховка от случайных выходов за корень проекта. Не заменяет файловые права.
  • disable_functions: отключите всё, что не используется (особенно исполнение команд). Для бэкенд‑тасков, где это нужно, заведите отдельный пул.
  • В Nginx не проксируйте произвольные пути в PHP. Разрешайте только \.php$ и используйте try_files $uri =404;.
  • Скрывайте версию PHP: expose_php = Off в php.ini или через админ‑значения пула. Для публичных сайтов включайте HTTPS и HSTS; о миграции на HTTPS и безопасных редиректах читайте: domain-migration-301-hsts-ssl. Если нужен выпуск и установка TLS, используйте наши SSL-сертификаты.
FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Не используйте один общий пул для всех сайтов, даже если версия PHP совпадает. Разделяйте пулы по проектам — это уменьшает blast radius и упрощает поиск утечек.

Мониторинг и диагностика

В пулах выше мы включили pm.status_path и ping.path. Их удобно опубликовать в Nginx только для локаля или внутренней сети. Минимум — ограничьте доступ по IP.

location = /fpm-ping { include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass unix:/run/php/php8.3-fpm-site1.sock; allow 127.0.0.1; deny all; }
location = /fpm-status { include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass unix:/run/php/php8.3-fpm-site1.sock; allow 127.0.0.1; deny all; }

Логи: error_log и access_log Nginx, плюс /var/log/php*-fpm.log и slowlog пула. Для быстрых проверок используйте:

sudo journalctl -u php8.3-fpm -f
sudo journalctl -u php8.1-fpm -f
sudo tail -f /var/log/nginx/site1.error.log

Частые ошибки и как их чинить

  • Permission denied к сокету FPM. Симптом: connect() to unix:/run/php/....sock failed (13). Проверьте listen.owner/listen.group/listen.mode пула и пользователя, под которым работает Nginx (обычно www-data).
  • No input file specified. Почти всегда неверный SCRIPT_FILENAME. Убедитесь, что равен $document_root$fastcgi_script_name, и try_files $uri =404; передаёт реальный файл.
  • 413 Request Entity Too Large. Увеличьте client_max_body_size в серверном блоке Nginx и post_max_size/upload_max_filesize в пуле.
  • 504 Gateway Timeout. Сверьте fastcgi_read_timeout и max_execution_time/request_terminate_timeout. Если фоновые задачи долгие — вынесите в очередь/воркеры.
  • Падение пула из‑за памяти. Уменьшите pm.max_children, оптимизируйте расширения, проверьте opcache.memory_consumption и лимиты.

Управление жизненным циклом: деплой, релоды, миграции

При выкладке: сначала синхронизируйте код вне document_root, затем переключите симлинк current на новую версию, и уже после этого выполните reload нужного пула для инвалидации opcache. Это гарантирует отсутствие «гонок» и поломанных инклюдов. Для очередей/воркеров на PHP заведите отдельные пулы и systemd‑юниты — не смешивайте их с пулом фронтенда. Подробно про безостановочную миграцию читайте: zero-downtime-site-migration.

Когда лучше TCP вместо Unix‑сокетов

Unix‑сокеты предпочтительны для одного хоста с Nginx и FPM. Если планируете вынести FPM на соседний хост, используйте TCP‑порт и ограничение firewall. В пределах одной машины TCP может пригодиться для инструментов, которым сложно работать с правами UNIX‑сокетов. По производительности на современных ядрах разницы почти нет, но сокеты всё же чуть быстрее и проще в настройке прав.

Проверочный чек‑лист

  • Для каждого сайта есть свой системный пользователь и директория проекта.
  • Для каждого сайта создан отдельный пул php-fpm нужной версии.
  • Сокеты лежат в /run/php, права на них выверены.
  • В Nginx каждое server указывает на свой fastcgi_pass.
  • Параметры загрузок и таймауты согласованы между Nginx и FPM.
  • Включены логи и slowlog, статус и пинг ограничены по IP.
  • CLI‑команды и cron вызывают конкретные версии php8.X.

Итог

Многоверсионная конфигурация PHP на одном сервере с Nginx несложна: по пулу на сайт, по версии на пул, чёткие права и минимально необходимые привилегии. Такой подход даёт чистую изоляцию, гибкий тюнинг под нагрузку и предсказуемые релизы. Главное — дисциплина: не смешивать сайты в одном пуле, не давать лишних прав и регулярно проверять логи. Тогда переходы между версиями PHP станут рутинной задачей вместо риска для всего сервера.

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

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

Grafana Tempo + Loki + Prometheus: корреляция traceID и быстрый triage инцидентов OpenAI Статья написана AI (GPT 5)

Grafana Tempo + Loki + Prometheus: корреляция traceID и быстрый triage инцидентов

Пошагово связываем Grafana Tempo, Loki и Prometheus в единую observability-схему: OpenTelemetry-трейсы, логи с traceID и метрики. ...
Kubernetes DNS: таймауты, MTU и conntrack — диагностика через CoreDNS, tcpdump и PMTUD OpenAI Статья написана AI (GPT 5)

Kubernetes DNS: таймауты, MTU и conntrack — диагностика через CoreDNS, tcpdump и PMTUD

Если в Kubernetes периодически не резолвится DNS или внешние API отвечают timeout, причина часто в MTU/PMTUD, conntrack и настройк ...
Kubernetes CrashLoopBackOff: события, пробы, exit codes и backoff — практический разбор OpenAI Статья написана AI (GPT 5)

Kubernetes CrashLoopBackOff: события, пробы, exit codes и backoff — практический разбор

CrashLoopBackOff в Kubernetes — не «ошибка», а симптом: контейнер быстро завершается, kubelet перезапускает его и увеличивает пауз ...