LAMP и LEMP – это две сборки для PHP-сайта на своём арендованном сервере. LAMP: Linux, Apache, MySQL, PHP. LEMP: то же самое, только вместо Apache стоит nginx (буква E от произношения «энджин-икс»). Буква M давно означает «MySQL или MariaDB», и в Debian по умолчанию будет как раз MariaDB.
Первые два раздела ниже дают готовые команды для обоих вариантов, этого хватит, чтобы поднять сервер под WordPress или другой PHP-движок. Дальше разбор, чем стеки отличаются на самом деле (меньше, чем принято думать), почему старые инструкции из интернета ломаются на свежей системе и что делать с ошибками 502, 403 и 413, которые рано или поздно увидит каждый.
Команды проверены под Ubuntu 24.04 и 26.04, Debian 12 и 13. Предполагается, что ты уже подключился к серверу по SSH, работаешь от обычного пользователя с sudo и включил файрвол. Если нет, начни с базовой настройки VPS: там это по шагам и в безопасном порядке.
LEMP за десять минут: nginx, PHP-FPM и база
1. Веб-сервер и PHP. Ставь именно пакет php-fpm, а не голый php. Почему это важно, расскажу ниже, в разделе про старые инструкции.
sudo apt update sudo apt install -y nginx php-fpm php-mysql php-curl php-gd php-mbstring php-xml php-zip php-intl
Список расширений после php-fpm рассчитан на WordPress и большинство популярных движков. Лишнее не навредит, а без php-mysql сайт просто не увидит базу.
2. База данных. На Debian доступна только MariaDB, на Ubuntu можно выбрать:
sudo apt install -y mariadb-server
или на Ubuntu, если нужен именно MySQL:
sudo apt install -y mysql-server
3. Узнай имя сокета PHP-FPM. Через этот файл nginx передаёт PHP-запросы, и в его имени зашита версия PHP:
ls /run/php/
На Ubuntu 24.04 увидишь php8.3-fpm.sock, на 26.04 php8.5-fpm.sock, на Debian 13 php8.4-fpm.sock, на Debian 12 php8.2-fpm.sock. Запомни свою строку, она нужна в конфиге.
4. Папка сайта. Вместо example.com везде подставь свой домен:
sudo mkdir -p /var/www/example.com sudo chown -R $USER:$USER /var/www/example.com
5. Конфиг сайта. Открой новый файл:
sudo nano /etc/nginx/sites-available/example.com
и вставь в него (версию в строке fastcgi_pass поменяй на свою из шага 3):
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com;
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.3-fpm.sock;
}
location ~ /\.(?!well-known) {
deny all;
}
} Последний блок закрывает доступ к скрытым файлам вроде .git и .env, но оставляет открытой папку .well-known, через которую потом будет выпускаться SSL-сертификат.
6. Включи сайт и проверь конфиг.
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/ sudo rm /etc/nginx/sites-enabled/default sudo nginx -t sudo systemctl reload nginx sudo ufw allow 80,443/tcp
Команда nginx -t должна ответить syntax is ok и test is successful. Если она ругается, reload не запускай: nginx продолжит работать со старым конфигом, и это лучше, чем уронить его совсем.
7. Проверь, что PHP отвечает.
echo '<?php phpinfo();' > /var/www/example.com/info.php
Открой http://example.com/info.php. Должна появиться большая таблица, в строке Server API будет FPM/FastCGI. После проверки файл удали:
rm /var/www/example.com/info.php
Это не формальность. В логах любого публичного сервера видно, как боты по кругу стучатся в /info.php, /phpinfo.php и /test.php: забытый файл выдаёт версии, пути и список модулей, т.е. готовую памятку для взлома.
LAMP: Apache, но сразу с PHP-FPM
Большинство инструкций для LAMP предлагают поставить пакет libapache2-mod-php, и PHP начинает работать внутри самого Apache. Это работает, но тянет за собой устаревший режим Apache, о нём в следующем разделе. Схема ниже даёт тот же LAMP, только PHP живёт отдельно, как в LEMP.
sudo apt update sudo apt install -y apache2 php-fpm php-mysql php-curl php-gd php-mbstring php-xml php-zip php-intl sudo a2enmod proxy_fcgi setenvif rewrite sudo a2enconf php8.3-fpm sudo systemctl restart apache2
Версию в a2enconf подставь свою. На удивление, сам пакет PHP-FPM при установке печатает в терминал подсказку ровно с этими командами (строка начинается с NOTICE: Not enabling PHP FPM by default), но в потоке вывода apt её мало кто замечает.
Файл сайта /etc/apache2/sites-available/example.com.conf:
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/example.com
<Directory /var/www/example.com>
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
</VirtualHost> Включение и проверка:
sudo a2ensite example.com sudo a2dissite 000-default sudo apache2ctl configtest sudo systemctl reload apache2 sudo ufw allow 80,443/tcp sudo apache2ctl -M | grep -E 'mpm|php'
Последняя команда должна показать mpm_event_module и не показать php_module. Если видишь mpm_prefork_module, значит где-то в системе всё-таки стоит модуль PHP для Apache. База данных и проверка через info.php такие же, как в LEMP выше.
Строка AllowOverride All нужна не для красоты. В стандартном apache2.conf на Debian и Ubuntu для /var/www/ стоит AllowOverride None, и Apache молча игнорирует файлы .htaccess. У WordPress из-за этого открывается главная, а все остальные страницы отдают 404, хотя .htaccess лежит на месте и правила в нём верные.
Чем LAMP отличается от LEMP на самом деле
«nginx быстрее, это все знают». В старых статьях (и в прошлой редакции этой тоже) разница объясняется так: Apache на каждый запрос запускает отдельный процесс, а nginx работает на событиях и поэтому экономнее. Правдой это было для связки Apache с модулем mod_php. Этот модуль нормально работает только в режиме mpm_prefork, где каждое соединение действительно держит целый процесс Apache, и в каждый такой процесс загружен весь PHP. Даже запрос картинки обслуживает процесс, который весит как PHP-воркер.
Когда Apache работает в режиме mpm_event и отдаёт PHP в PHP-FPM, эта разница почти исчезает. Оба веб-сервера передают PHP-код одному и тому же PHP-FPM, и страница WordPress собирается одинаково быстро в обоих случаях. Скорость динамического сайта определяют PHP, база и кэш, а не веб-сервер перед ними.
Настоящие различия лежат в другом месте:
| Apache + PHP-FPM | nginx + PHP-FPM | |
|---|---|---|
| Скорость PHP-страниц | одинаковая: страницы собирает PHP-FPM | одинаковая |
| Статика и тысячи медленных соединений | справляется, но памяти уходит больше | заметно экономнее |
| Файлы .htaccess | читает, плагины и движки сами дописывают туда правила | не читает, все правила только в конфиге сервера |
| Где спотыкаются новички | AllowOverride и выключенный mod_rewrite | регулярные выражения в location и try_files |
| Когда брать | старые движки и плагины, завязанные на .htaccess | новые проекты, WordPress с кэшем страниц |
Разница в .htaccess для новичка самая ощутимая. На Apache плагин редиректов или кэширования сам пишет правила в файл, и они сразу работают. На nginx то же самое придётся переносить в конфиг руками, через sudo и перезагрузку. Кстати, отсюда же тихая проблема с некоторыми плагинами безопасности для WordPress: на nginx они старательно пишут запреты в .htaccess, в админке всё зелёное, а nginx этот файл даже не открывает.
Есть и промежуточные варианты. Можно поставить nginx на вход, а Apache за ним, чтобы nginx отдавал статику, а Apache обрабатывал .htaccess. Как это собрать, разобрано в статье про Nginx и Apache на одном сервере. Сегодня эта схема имеет смысл в основном ради совместимости: два веб-сервера вместо одного дают вдвое больше конфигов. Ещё есть OpenLiteSpeed, который понимает большую часть правил из .htaccess и дружит с плагином LSCache (его удобнее всего ставить через CyberPanel), и Caddy, который сам получает SSL-сертификаты.
Если сомневаешься, бери LEMP. Если есть конкретная причина держаться за .htaccess, бери Apache с PHP-FPM и не переживай из-за выбора: база и PHP в обоих случаях те же, а переезд с одного веб-сервера на другой обычно занимает вечер.
Почему старые инструкции ломаются на свежей системе
После выхода Ubuntu 24.04 обращения шли пачками по одной схеме: человек обновил систему с 22.04, сайт отдаёт 502, nginx при этом жив и перезапускается без ошибок. В логе строка такого вида:
connect() to unix:/run/php/php8.1-fpm.sock failed (2: No such file or directory) while connecting to upstream
Вместе с системой PHP обновился с 8.1 до 8.3, старый PHP-FPM удалился, а в конфиге сайта остался путь к сокету, которого больше нет. Ровно то же происходит, когда инструкцию пятилетней давности с php7.4-fpm.sock копируют на свежий сервер. Версии в стандартных репозиториях сейчас такие:
| Система | PHP | База по умолчанию | Сокет PHP-FPM |
|---|---|---|---|
| Ubuntu 24.04 LTS | 8.3 | MySQL 8.0 или MariaDB | /run/php/php8.3-fpm.sock |
| Ubuntu 26.04 LTS | 8.5 | MySQL 8.4 или MariaDB | /run/php/php8.5-fpm.sock |
| Debian 12 | 8.2 | MariaDB 10.11 | /run/php/php8.2-fpm.sock |
| Debian 13 | 8.4 | MariaDB 11.8 | /run/php/php8.4-fpm.sock |
На Debian и Ubuntu есть ещё универсальный путь /run/php/php-fpm.sock. Это ссылка, которая указывает на сокет самой новой установленной версии PHP, и конфиг с ней переживает смену версии. Обратная сторона: если ради теста поставить второй, более свежий PHP, все сайты с такой ссылкой тихо переедут на него. Для одного сайта на сервере это удобно, для нескольких я бы писал версию явно. Перед апгрейдом системы в любом случае стоит выписать, какие сокеты указаны в конфигах.
Вторая ловушка менее очевидная. Метапакет php в Debian и Ubuntu требует хоть какой-то способ запускать PHP на веб-сервере и первым вариантом в зависимостях у него стоит модуль для Apache. Если выполнить apt install php отдельной командой, apt честно поставит libapache2-mod-php и сам Apache, который тут же займёт 80-й порт. Потом ставишь nginx, а он не стартует:
bind() to 0.0.0.0:80 failed (98: Address already in use)
Если это уже случилось, лишний Apache убирается так:
sudo systemctl disable --now apache2 sudo apt purge -y apache2 'libapache2-mod-php*' sudo systemctl restart nginx
Третий привет из прошлого касается базы. В старых советах по оптимизации MySQL почти всегда есть query_cache_size. Честно скажу, в прошлой версии этой статьи он тоже был. Кэш запросов удалили ещё в MySQL 8.0, и если вписать этот параметр в конфиг, сервер базы просто не запустится, а в логе будет unknown variable 'query_cache_size'. В MariaDB параметр остался, но по умолчанию выключен, и включать его на типичном сайте смысла мало…
Если у тебя AlmaLinux или Rocky Linux, логика та же, отличаются названия. Веб-сервер там называется httpd, пакеты ставятся через dnf, Apache по умолчанию уже работает с PHP-FPM, сокет лежит в /run/php-fpm/www.sock, а вместо ufw используется firewalld. Плюс включённый SELinux, из-за которого, например, Apache не может достучаться до базы на другом сервере, пока не включишь нужный флаг. Подробнее про него в разборе SELinux и AppArmor, а про выбор дистрибутива в статье какую ОС выбрать для VPS.
MySQL или MariaDB и первые минуты с базой
Для WordPress и большинства PHP-движков разницы нет, они одинаково работают с обеими. Выбор становится важен при переезде. MySQL 8 по умолчанию создаёт таблицы со схемой сортировки utf8mb4_0900_ai_ci. MariaDB научилась её понимать только с версии 11.4.5, а в Debian 12 и Ubuntu 24.04 стоит 10.11. Дамп со старого хостинга на MySQL 8 при импорте в такую MariaDB падает с ошибкой Unknown collation: 'utf8mb4_0900_ai_ci', и дальше начинается правка дампа. Поэтому, если переносишь сайт, проще поставить ту же СУБД, что была у хостинга. Для нового проекта на маленьком VPS я бы брал MariaDB: на 1–2 ГБ памяти она обычно ведёт себя спокойнее.
Дальше первое, что вызывает вопросы. Ты только что поставил базу, пароль root нигде не задавал, а команда
sudo mysql
пускает внутрь без всякого пароля (в MariaDB 11 то же самое делает sudo mariadb, старое имя там оставлено для совместимости). Выглядит как дыра, но это как раз защита: на Debian и Ubuntu root базы входит через сокет, т.е. только от системного root или через sudo, и подобрать пароль к нему по сети невозможно в принципе. Скрипт sudo mysql_secure_installation (в MariaDB он называется mariadb-secure-installation) всё равно стоит запустить: он уберёт анонимных пользователей и тестовую базу. А вопрос про пароль root на Ubuntu с MySQL можно пропустить, при входе через сокет этот пароль ни на что не влияет.
Для сайта заводится отдельная база и отдельный пользователь. Внутри sudo mysql:
CREATE DATABASE site_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'site_user'@'localhost' IDENTIFIED BY 'длинный_случайный_пароль'; GRANT ALL PRIVILEGES ON site_db.* TO 'site_user'@'localhost'; EXIT;
Кодировку utf8mb4_unicode_ci я указываю явно ровно из-за истории с переездом выше: такую базу без проблем примут и MySQL, и MariaDB.
Порт 3306 наружу открывать не нужно. На Debian и Ubuntu база по умолчанию слушает только 127.0.0.1, сайт на том же сервере до неё дотягивается, а из интернета её не видно. Если нужно подключиться к базе со своего компьютера, для этого есть SSH-туннель, подробности в статье про безопасное удалённое подключение к базе.
Про память. Главная настройка InnoDB, innodb_buffer_pool_size, по умолчанию равна 128 МБ. Популярный совет «ставь 70% памяти» написан для сервера, где работает только база. На VPS с полным стеком ту же память делят PHP-FPM, веб-сервер и система, и 70% от 2 ГБ там задушат всё остальное. Разумнее посмотреть, сколько весят твои базы:
SELECT table_schema, ROUND(SUM(data_length + index_length) / 1024 / 1024) AS mb FROM information_schema.tables GROUP BY table_schema;
Если сайт весит 200–300 МБ, буфера в 256–512 МБ ему обычно хватает с запасом. Параметр прописывается в секции [mysqld] файла /etc/mysql/mysql.conf.d/mysqld.cnf для MySQL или /etc/mysql/mariadb.conf.d/50-server.cnf для MariaDB, после чего базу нужно перезапустить. Остальные параметры и порядок их подбора разобраны в статье про оптимизацию MySQL на небольшом сервере.
На VPS с 1 ГБ памяти и MySQL 8 стоит заглянуть в sudo dmesg, если база вдруг «сама» перезапустилась ночью. Строка Out of memory: Killed process ... (mysqld) значит, что память кончилась и ядро выбрало жертву покрупнее. Лечится это либо переходом на тариф побольше, либо хотя бы swap-файлом, который даст системе запас на пики.
PHP-FPM: пять процессов на весь сайт
Самое неприятное в настройках по умолчанию прячется в файле /etc/php/8.3/fpm/pool.d/www.conf (путь с твоей версией). Там стоит pm.max_children = 5. Это значит, что одновременно сервер выполняет не больше пяти PHP-запросов, а шестой ждёт в очереди, пока освободится один из пяти.
Для блога с десятком посетителей в час этого хватает. А вот магазин во время рассылки выглядит странно: процессор загружен на 20%, память свободна, а страницы открываются по десять секунд или отдают 504. В логе /var/log/php8.3-fpm.log в такой момент находится строка:
WARNING: [pool www] server reached pm.max_children setting (5), consider raising it
Поднимать лимит вслепую тоже нельзя, каждый процесс занимает память. Сначала посмотри, сколько весит один:
ps -o rss= -C php-fpm8.3 | awk '{s+=$1} END {print s/NR/1024 " MB"}' Для WordPress с парой десятков плагинов это обычно 40–100 МБ на процесс, у тяжёлых магазинов бывает и больше. Дальше простая арифметика: из общей памяти вычти то, что занимают база и система, остаток раздели на вес процесса. На VPS с 2 ГБ, где база с системой забирают около 700 МБ, а процесс весит 80 МБ, выходит примерно 15 воркеров. Настройки в www.conf тогда могут выглядеть так:
pm = dynamic pm.max_children = 15 pm.start_servers = 4 pm.min_spare_servers = 2 pm.max_spare_servers = 6
После правки нужен sudo systemctl restart php8.3-fpm. Подробный разбор режимов dynamic, ondemand и static есть в статье про настройку PHP-FPM.
Второй сюрприз связан с тем, что файлов php.ini в системе несколько. Для PHP-FPM это /etc/php/8.3/fpm/php.ini, для командной строки /etc/php/8.3/cli/php.ini. Человек правит лимит загрузки, проверяет через php -i в терминале, видит новое значение и не понимает, почему сайт по-прежнему не принимает файлы. Команда php -i показывает настройки CLI, а сайт работает через FPM. Правь файл из папки fpm и проверяй через info.php в браузере.
Кстати, про загрузку файлов. По умолчанию PHP принимает файлы до 2 МБ (upload_max_filesize) и формы до 8 МБ (post_max_size), а nginx ещё раньше режет всё тяжелее 1 МБ и отдаёт 413 Request Entity Too Large. Чтобы загрузить через админку тему или архив на 50 МБ, лимит нужно поднять в обоих местах:
# в /etc/php/8.3/fpm/php.ini upload_max_filesize = 64M post_max_size = 64M # в блоке server конфига nginx client_max_body_size 64M;
Пока info.php ещё на месте, найди в нём раздел Zend OPcache. В пакетах Debian и Ubuntu он обычно уже включён, и это самое дешёвое ускорение PHP, какое бывает: скрипты не разбираются заново на каждом запросе. Если раздела нет, поставь пакет php8.3-opcache и перезапусти PHP-FPM. В PHP 8.5 OPcache стал обязательной частью самого PHP, и ставить его отдельно не нужно.
502, 403 и PHP, который скачивается: разбор по симптомам
Почти любая поломка стека видна в одном из трёх логов: /var/log/nginx/error.log (или /var/log/apache2/error.log), /var/log/php8.3-fpm.log и журнал службы через journalctl -u php8.3-fpm. Прежде чем что-то менять, стоит заглянуть туда, т.к. текст ошибки почти всегда называет причину прямо.
| Что видишь | Частая причина | Что проверить |
|---|---|---|
| 502 Bad Gateway (nginx) или 503 (Apache) | PHP-FPM не запущен или в конфиге указан чужой сокет | systemctl status php8.3-fpm, путь в fastcgi_pass или SetHandler |
| 504 или страницы открываются очень долго | все воркеры PHP-FPM заняты | строка про pm.max_children в логе PHP-FPM |
| Браузер скачивает PHP-файл вместо страницы | веб-сервер не передаёт .php в PHP-FPM | блок location ~ \.php$ или команду a2enconf |
| 403 Forbidden на главной | в index нет index.php или у веб-сервера нет прав на чтение | директиву index, права на папку сайта |
| Главная работает, остальные страницы 404 | не настроены ЧПУ | try_files в nginx или AllowOverride и mod_rewrite в Apache |
| 413 Request Entity Too Large | лимит nginx на размер запроса | client_max_body_size |
| nginx не стартует, Address already in use | 80-й порт занят Apache | sudo ss -tlnp | grep ':80' |
| Error establishing a database connection | неверные имя, пароль или база не запущена | systemctl status mariadb или mysql, данные в wp-config.php |
Одна строчка из этой таблицы коварнее остальных. Когда PHP-файл скачивается вместо того, чтобы открыться, браузер иногда запоминает это поведение. Конфиг уже исправлен, а на твоём компьютере страница по-прежнему «скачивается», и кажется, что правка не помогла. Проверяй в приватном окне или через curl -I http://example.com/: если в ответе есть Content-Type: text/html, сервер работает правильно, а дело в кэше браузера.
С 403 похожая история наоборот. Права на папку выглядят нормально, index.php на месте, а ошибка остаётся. Чаще всего выясняется, что файлы распаковали в /root или в домашнюю папку с правами 700 и сделали на них ссылку из /var/www: веб-серверу нужно право заходить в каждую папку по пути к файлу, а не только в последнюю. Как устроены права и почему не стоит отдавать все файлы сайта пользователю www-data, разобрано в статье про chmod и chown.
Что сделать сразу после стека
Стек готов, но сайт пока работает по голому HTTP. Следующим шагом выпусти бесплатный сертификат Let’s Encrypt через Certbot: он сам допишет в конфиг nginx или Apache всё нужное для HTTPS. Потом настрой резервное копирование и отдельно автоматический бэкап базы, ведь снапшот диска с работающей базой не всегда восстанавливается целым.
Дальше по задаче: установка WordPress на VPS, несколько сайтов на одном сервере или ускорение загрузки. А если после этой статьи стало понятно, что конфиги руками тебе не по душе, это нормальный вывод, а не поражение. Ту же связку за пару кликов собирают панели управления, и понимание того, что у них внутри, пригодится, когда панель однажды покажет 502.
Источники
- nginx: модуль ngx_http_fastcgi_module
- nginx: директива client_max_body_size
- Apache HTTP Server: mod_proxy_fcgi
- Apache HTTP Server: модули MPM
- Apache HTTP Server: файлы .htaccess
- PHP Manual: конфигурация PHP-FPM
- MySQL 8.0 Reference Manual: что изменилось в MySQL 8.0
- MySQL 8.4 Reference Manual: innodb_buffer_pool_size
- Ubuntu 26.04 LTS: примечания к выпуску
- Debian 13: что нового в выпуске
- WordPress: системные требования

