Несколько версий PHP и PHP-FPM на одном сервере: Debian, Ubuntu и AlmaLinux

Держать несколько версий PHP на одном сервере сегодня просто. Каждая версия ставится отдельным пакетом и запускает свой сервис PHP-FPM со своим сокетом. Веб-сервер для каждого сайта передаёт PHP-запросы в нужный сокет. Так один сайт работает на PHP 8.4, а старый движок рядом живёт на 8.2, и они друг другу не мешают.

Самый частый рецепт такой. На Debian подключаем репозиторий Ондржея Сури (packages.sury.org), на Ubuntu — его PPA ppa:ondrej/php, на AlmaLinux и Rocky Linux — репозиторий Remi. Ставим пакеты php8.x-fpm (или php84-php-fpm на AlmaLinux), создаём для сайта отдельный пул от отдельного пользователя и прописываем путь к сокету в Nginx (fastcgi_pass) или в Apache (SetHandler "proxy:unix:...").

Статья заменяет две старые заметки 2012–2017 годов: про установку PHP 5.4 на CentOS 5/6 из Remi и Atomic и про запуск PHP через mod_fcgid. Те инструкции больше не работают, а сами версии давно без поддержки. Ниже — как то же самое делается на Debian 12 и 13, Ubuntu 24.04 и AlmaLinux / Rocky Linux 9 и 10.

Какие версии PHP есть в дистрибутивах

У каждой системы своя версия PHP «из коробки». Если она подходит всем сайтам, сторонний репозиторий не нужен. Он нужен, когда одному сайту требуется версия новее, а другому — старше системной.

СистемаPHP из основного репозиторияОткуда брать другие версииИмя сервиса FPM
Debian 12 (bookworm)8.2packages.sury.org/phpphp8.2-fpm, php8.4-fpm и т. д.
Debian 13 (trixie)8.4packages.sury.org/phpphp8.4-fpm и т. д.
Ubuntu 24.04 LTS8.3ppa:ondrej/phpphp8.3-fpm и т. д.
AlmaLinux / Rocky 98.0, новее — потоки модуля php в AppStreamRemi (rpms.remirepo.net)php-fpm (системная), php84-php-fpm (параллельная)
AlmaLinux / Rocky 108.3Remiphp-fpm, php84-php-fpm

Поддерживаемые ветки PHP на октябрь 2026 года: 8.2 (только исправления безопасности до конца 2026), 8.3, 8.4 и 8.5. Всё, что старше 8.2, уже не получает исправлений. Актуальные сроки смотрите на странице php.net/supported-versions. Старую версию держите только как временную меру, пока сайт не обновят.

Почему PHP-FPM, а не mod_php и не старый FastCGI

В старой статье PHP подключали к Apache через mod_fcgid, а ещё раньше — модулем mod_php. Сейчас стандарт — PHP-FPM. Причины практические:

  • Несколько версий. mod_php загружает в Apache одну версию PHP на весь сервер. С FPM каждая версия — отдельный сервис.
  • Разные пользователи. С mod_php все сайты выполняются от пользователя Apache (www-data или apache). Взлом одного сайта даёт доступ к файлам остальных. В FPM у каждого пула свой пользователь.
  • MPM event. mod_php требует MPM prefork: один процесс Apache на соединение, и в каждом процессе висит PHP. С FPM Apache работает в режиме event и отдаёт статику, не тратя память на PHP.
  • Управление процессами. FPM сам держит пул воркеров, перезапускает их после pm.max_requests, пишет slow-log и статус. mod_fcgid тоже работает, но процессы им запускает Apache, и настройки пулов там беднее. Модуль mod_fastcgi давно заброшен.

Совет из старой статьи закомментировать session.save_path в php.ini больше не нужен. С FPM путь к сессиям задаётся в настройках пула, отдельно для каждого сайта (см. ниже).

Debian 12 и 13: репозиторий Sury

Ондржей Сури — сопровождающий пакетов PHP в Debian. Его репозиторий собран так же, как штатные пакеты, и содержит все поддерживаемые ветки PHP. Подключаем его по инструкции из README репозитория:

apt update
apt install -y lsb-release ca-certificates curl
curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb
dpkg -i /tmp/debsuryorg-archive-keyring.deb
echo "deb [signed-by=/usr/share/keyrings/debsuryorg-archive-keyring.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list
apt update

Ставим нужные версии. Расширения у каждой версии свои, их нужно ставить для каждой версии отдельно:

apt install php8.4-fpm php8.4-cli php8.4-mysql php8.4-curl php8.4-gd php8.4-mbstring php8.4-xml php8.4-zip php8.4-intl
apt install php8.2-fpm php8.2-cli php8.2-mysql php8.2-curl php8.2-gd php8.2-mbstring php8.2-xml php8.2-zip php8.2-intl

Не ставьте пакеты без номера версии вроде php-redis или php-mysql, если версий несколько. Это метапакеты: они тянут расширение только для версии по умолчанию. Нужно php8.2-redis, php8.4-redis и так далее.

Где что лежит у версии 8.4 (для других версий меняется только номер):

ЧтоПуть
php.ini для FPM/etc/php/8.4/fpm/php.ini
php.ini для консоли/etc/php/8.4/cli/php.ini
Пулы/etc/php/8.4/fpm/pool.d/*.conf
Сокет пула www/run/php/php8.4-fpm.sock
Ini-файлы расширений/etc/php/8.4/mods-available/, включаются через phpenmod
Сервисphp8.4-fpm.service

Ubuntu 24.04: PPA ondrej/php

Для Ubuntu тот же автор ведёт PPA. Он поддерживает только LTS-выпуски, 24.04 в их числе:

apt install -y software-properties-common
add-apt-repository ppa:ondrej/php
apt update
apt install php8.4-fpm php8.4-cli php8.4-mysql php8.4-mbstring php8.4-xml

Пути и имена сервисов те же, что в Debian. Системная версия на 24.04 — 8.3. После подключения PPA она тоже начнёт обновляться из PPA, это нормально.

AlmaLinux и Rocky Linux 9 и 10: репозиторий Remi

Remi Collet сопровождает PHP в Fedora и RHEL. Его репозиторий — то, что в старой статье подключали по адресу rpms.famillecollet.com. Подробно про EPEL и Remi — в статье Репозитории EPEL и Remi для AlmaLinux и Rocky Linux. Коротко для AlmaLinux 9:

dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm
dnf install https://rpms.remirepo.net/enterprise/remi-release-9.rpm
crb install

Для версии 10 замените в обеих ссылках 9 на 10. Команда crb install появляется вместе с пакетом epel-release и включает репозиторий CRB.

Дальше есть два пути. Remi предлагает их в мастере настройки.

Одна версия вместо системной

Если на сервере нужна одна версия, но новее штатной, переключаем модуль php на поток Remi. Пакеты сохраняют обычные имена и пути:

dnf module switch-to php:remi-8.4/common
dnf install php-fpm php-cli php-mysqlnd php-mbstring php-xml php-intl
systemctl enable --now php-fpm

Конфиги: /etc/php.ini, /etc/php.d/, пулы в /etc/php-fpm.d/, сокет пула www — /run/php-fpm/www.sock.

Несколько версий параллельно

Для параллельной установки Remi собирает версии как Software Collections: у пакетов префикс php82-, php84- и так далее. Они лежат в репозитории remi-safe, который включён сразу после установки remi-release. Системный PHP при этом не трогается.

dnf install php84 php84-php-fpm php84-php-mysqlnd php84-php-mbstring php84-php-xml
dnf install php82 php82-php-fpm php82-php-mysqlnd php82-php-mbstring php82-php-xml
systemctl enable --now php84-php-fpm php82-php-fpm
ЧтоПуть для php84
php.ini/etc/opt/remi/php84/php.ini
Ini-файлы расширений/etc/opt/remi/php84/php.d/
Пулы/etc/opt/remi/php84/php-fpm.d/*.conf
Сокет пула www/var/opt/remi/php84/run/php-fpm/www.sock
Консольный PHP/usr/bin/php84
Сервисphp84-php-fpm.service

На AlmaLinux включён SELinux, и выключать его не нужно. Сокеты в штатных каталогах уже имеют правильный контекст. Если веб-сервер не может подключиться к сокету или прочитать файлы сайта, смотрите отказы командой ausearch -m avc -ts recent и правьте контекст через semanage fcontext и restorecon. Типичный случай — сайт в /home или /srv без контекста httpd_sys_content_t.

Пулы PHP-FPM: отдельный пользователь на каждый сайт

Пул — группа процессов FPM со своими настройками: пользователь, сокет, лимиты, значения php.ini. Один сайт — один пул. Тогда сайт работает от своего пользователя, а его падение или зависание не трогает соседей.

Создаём пользователя без входа в систему и каталог сайта:

useradd --system --home-dir /var/www/site1 --shell /usr/sbin/nologin site1
mkdir -p /var/www/site1/public /var/www/site1/tmp /var/www/site1/sessions
chown -R site1:site1 /var/www/site1

Файл пула на Debian и Ubuntu — /etc/php/8.4/fpm/pool.d/site1.conf:

[site1]
user = site1
group = site1

listen = /run/php/php8.4-fpm-site1.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = ondemand
pm.max_children = 10
pm.process_idle_timeout = 10s
pm.max_requests = 500

php_admin_value[open_basedir] = /var/www/site1:/usr/share/php
php_admin_value[upload_tmp_dir] = /var/www/site1/tmp
php_admin_value[sys_temp_dir] = /var/www/site1/tmp
php_admin_value[session.save_path] = /var/www/site1/sessions
php_admin_value[error_log] = /var/www/site1/php-error.log
php_admin_flag[log_errors] = on

Что здесь важно:

  • user и group — от кого выполняется PHP-код сайта.
  • listen.owner, listen.group, listen.mode — кто может писать в сокет. Веб-сервер должен иметь доступ, остальные — нет. Для Nginx на Debian это тоже www-data.
  • pm = ondemand подходит для небольших сайтов: процессы стартуют по запросу и гаснут при простое. Для нагруженного сайта берите pm = dynamic и подбирайте pm.max_children по памяти: средний размер процесса PHP, умноженный на число процессов, должен помещаться в RAM с запасом.
  • php_admin_value задаёт значения, которые сайт не может переопределить через ini_set().

На AlmaLinux пул кладут в /etc/php-fpm.d/site1.conf (системный PHP) или в /etc/opt/remi/php84/php-fpm.d/site1.conf (параллельная версия). Вместо listen.owner там принято использовать ACL: в штатном www.conf стоит listen.acl_users = apache,nginx. Скопируйте эту строку в свой пул, а сокет разместите рядом со штатным, например /run/php-fpm/site1.sock.

Штатный пул www можно оставить или удалить. FPM не запустится, если в каталоге пулов не останется ни одного пула.

Проверяем конфиг и перезапускаем:

php-fpm8.4 -t
systemctl restart php8.4-fpm
ls -l /run/php/

На AlmaLinux проверка — php-fpm -t для системной версии. Для php84 проще перезапустить сервис и посмотреть systemctl status php84-php-fpm: при ошибке в конфиге там будет строка с номером.

Nginx: выбор версии PHP для сайта

Версию определяет путь к сокету в fastcgi_pass. На Debian и Ubuntu есть готовый сниппет snippets/fastcgi-php.conf:

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

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

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.4-fpm-site1.sock;
    }
}

Второй сайт на PHP 8.2 отличается только сокетом: unix:/run/php/php8.2-fpm-site2.sock. Чтобы перевести сайт на другую версию, создайте пул в каталоге новой версии, поменяйте fastcgi_pass и перезагрузите Nginx.

На AlmaLinux сниппета нет. Пишем блок вручную:

location ~ \.php$ {
    try_files $uri =404;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/var/opt/remi/php84/run/php-fpm/site1.sock;
}

Пакет php-fpm на AlmaLinux добавляет в Nginx файлы /etc/nginx/conf.d/php-fpm.conf и /etc/nginx/default.d/php.conf. Они направляют PHP сайта по умолчанию в системный сокет. Для своих виртуальных хостов они не мешают, но помните о них, если PHP вдруг выполняется не той версией. Подробности директив — в документации модуля ngx_http_fastcgi_module.

nginx -t && systemctl reload nginx

Apache 2.4: связка через proxy_fcgi

Apache передаёт запросы в FPM модулем mod_proxy_fcgi. Отдельные модули вроде mod_fcgid для этого не нужны.

Debian и Ubuntu

Если раньше стоял mod_php, отключаем его и prefork, включаем event и proxy_fcgi:

a2dismod php8.2 mpm_prefork
a2enmod mpm_event proxy_fcgi setenvif
a2enconf php8.4-fpm
apachectl configtest && systemctl restart apache2

Команда a2enconf php8.4-fpm включает файл из пакета FPM. Он направляет все .php в сокет пула www этой версии — это версия по умолчанию для сервера. Сайту с другой версией или своим пулом указываем сокет в виртуальном хосте:

<VirtualHost *:80>
    ServerName site2.example.ru
    DocumentRoot /var/www/site2/public

    <FilesMatch "\.php$">
        SetHandler "proxy:unix:/run/php/php8.2-fpm-site2.sock|fcgi://localhost"
    </FilesMatch>

    <Directory /var/www/site2/public>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

Блок FilesMatch внутри виртуального хоста перекрывает глобальный. Файлы .htaccess продолжают работать: их читает Apache. А вот директивы php_value в .htaccess с FPM не работают и вызывают ошибку 500. Переносите их в пул (php_admin_value) или в файл .user.ini в корне сайта.

AlmaLinux и Rocky Linux

Здесь Apache уже по умолчанию работает в режиме event и отдаёт PHP в FPM. Это задаёт файл /etc/httpd/conf.d/php.conf из пакета php: в нём SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost". Для сайта на версии Remi указываем её сокет в виртуальном хосте так же, как выше:

<FilesMatch "\.php$">
    SetHandler "proxy:unix:/var/opt/remi/php82/run/php-fpm/site2.sock|fcgi://localhost"
</FilesMatch>
apachectl configtest && systemctl reload httpd

Версия PHP в консоли: update-alternatives и cron

Веб-версию выбирает сокет, а консольную — команда php. На Debian и Ubuntu это ссылка через систему alternatives. Посмотреть и переключить:

update-alternatives --display php
update-alternatives --set php /usr/bin/php8.4
php -v

Интерактивно то же делает update-alternatives --config php. Если ставите пакеты для сборки расширений, переключите заодно phpize и php-config.

Есть подвох. В автоматическом режиме alternatives выбирает самую новую версию. Поставили php8.5 для теста — и все cron-задачи с php внезапно пошли на 8.5. Команда --set переводит ссылку в ручной режим, и такого не случится. Ещё надёжнее — писать в задачах полный путь к нужной версии. Пример файла /etc/cron.d/site1 (в этом формате есть поле с именем пользователя):

*/5 * * * * site1 /usr/bin/php8.4 /var/www/site1/public/wp-cron.php > /dev/null 2>&1

На AlmaLinux команда php — это системная версия (или выбранный поток модуля). Параллельные версии вызываются как php82, php84. Для Composer и WP-CLI запускайте их нужной версией явно: php84 /usr/local/bin/composer install.

Проверка результата

  1. Сервисы запущены: systemctl status php8.4-fpm php8.2-fpm (на AlmaLinux — php84-php-fpm).
  2. Сокеты на месте и с нужными правами: ls -l /run/php/. Примерно так: srw-rw---- 1 www-data www-data 0 ... php8.4-fpm-site1.sock.
  3. Процессы работают от пользователя сайта: ps -eo user,args | grep "[p]ool site1". Должны быть строки вида site1 php-fpm: pool site1.
  4. Сайт отвечает нужной версией. Создайте временный файл и сразу удалите после проверки:
echo '<?php echo PHP_VERSION, " ", get_current_user(), "\n";' > /var/www/site1/public/v.php
curl -s http://site1.example.ru/v.php
rm /var/www/site1/public/v.php

Ответ примерно такой: 8.4.x site1. Не оставляйте на сайте phpinfo(): он раскрывает пути, версии и переменные окружения.

Типичные проблемы

СимптомПричинаРешение
Nginx: 502 Bad Gateway, в логе connect() to unix:... failed (2: No such file or directory)Пул не запущен или путь к сокету с опечаткойsystemctl status php8.x-fpm, сверить listen в пуле и fastcgi_pass
Nginx: 502, в логе (13: Permission denied)У веб-сервера нет прав на сокетПоправить listen.owner/listen.group или listen.acl_users; на AlmaLinux проверить SELinux
Apache: 503 Service Unavailable, в логе attempt to connect to Unix domain socket ... failedТо же: сокета нет или нет правКак выше; проверить путь в SetHandler
Primary script unknown / «File not found.»Неверный SCRIPT_FILENAME или root; пользователь пула не может прочитать файлы; open_basedirПроверить root и права на каталог сайта для пользователя пула
Сайт показывает не ту версию PHPСрабатывает глобальный обработчик (a2enconf, default.d/php.conf), а не блок виртуального хостаПроверить, какой SetHandler или location применяется к сайту
Ошибки сессий: Permission denied в session_start()Пул от нового пользователя пишет в общий каталог сессийЗадать session.save_path в пуле на каталог сайта
Ошибка 500 после перехода с mod_phpphp_value или php_flag в .htaccessПеренести настройки в пул или .user.ini
PHP Startup: Unable to load dynamic libraryРасширение не поставлено для этой версии или ini ссылается на чужой модульСм. статью PHP Startup: Unable to load dynamic library — как найти и исправить

Как убрать старую версию

Когда последний сайт переехал на новую версию, старую лучше удалить: она перестаёт получать исправления безопасности. Сначала убедитесь, что её сокеты никто не использует:

grep -rn "php8.2" /etc/nginx /etc/apache2 /etc/cron* /var/spool/cron 2>/dev/null
apt purge 'php8.2-*'
apt autoremove

На AlmaLinux — dnf remove 'php82-*'. Перед удалением сохраните свои пулы и правки php.ini: при purge каталог /etc/php/8.2 удаляется вместе с ними.

Если сервер готовите под WordPress, дальнейшую настройку (кэш, OPcache, базу) смотрите в статье Настройка VPS для быстрой работы сайта WordPress.

Частые вопросы

Безопасно ли подключать репозиторий Sury или Remi на рабочий сервер?

Оба репозитория ведут люди, которые сопровождают PHP в самих дистрибутивах: Ондржей Сури — в Debian, Remi Collet — в Fedora и RHEL. Пакеты подписаны, обновления выходят быстро. Но это всё же сторонний источник: вы доверяете его ключу. Подключайте только официальные адреса packages.sury.org, ppa:ondrej/php и rpms.remirepo.net, не берите копии с зеркал и сайтов-инструкций.

Сколько версий PHP можно держать одновременно?

Технических ограничений нет: каждая версия — отдельный сервис. Ограничивает память. Каждый запущенный пул держит процессы, поэтому для редко используемых сайтов ставьте pm = ondemand. На практике хватает двух версий: текущей для всех сайтов и предыдущей для тех, что ещё не обновили.

Можно ли переключить версию PHP для сайта без простоя?

Да. Создайте пул сайта в каталоге новой версии, запустите или перезагрузите её FPM, проверьте, что сокет появился. Затем поменяйте сокет в конфиге сайта и выполните systemctl reload nginx или systemctl reload apache2. Перезагрузка конфигурации не обрывает текущие соединения. Если что-то пошло не так, верните старый сокет так же быстро.

Почему php -v показывает одну версию, а сайт работает на другой?

Это нормально. Консольный PHP выбирается ссылкой /usr/bin/php через update-alternatives, а сайт — сокетом в конфиге веб-сервера. Они не связаны. Для задач cron и консольных утилит сайта указывайте полный путь к нужной версии, например /usr/bin/php8.2 или php82 на AlmaLinux.

Нужен ли mod_fcgid или suPHP, чтобы сайты работали от разных пользователей?

Нет. В PHP-FPM это делает параметр user в пуле. suPHP давно не развивается, mod_fcgid можно встретить на старых серверах, но новые установки делают на FPM. Если на сервере ещё работает mod_fcgid, переводите сайты на пулы FPM по одному и затем отключайте модуль.

Как узнать, какие расширения стоят у каждой версии?

Выполните php8.2 -m и php8.4 -m на Debian и Ubuntu или php82 -m и php84 -m на AlmaLinux. Сравните списки перед переездом сайта на новую версию. Учтите, что у консоли и FPM могут быть разные ini-файлы, поэтому окончательную проверку делайте временным скриптом через сайт.