LAMP на Ubuntu 24.04: Apache, MariaDB и PHP-FPM
Пошагово соберите LAMP на Ubuntu 24.04: Apache, MariaDB с unix_socket, PHP 8.3 через PHP-FPM, виртуальный хост и HTTPS Let's Encrypt через Certbot.
Что вы создаёте
Стек LAMP состоит из четырёх компонентов на одном сервере Ubuntu 24.04: Linux в основе, Apache обрабатывает HTTP-запросы, MariaDB хранит данные, а PHP 8.3 выполняет код. В результате вы получите виртуальный хост на основе имени, обслуживающий каталог реального приложения, базу данных с отдельным пользователем с минимально необходимыми привилегиями, PHP, подключённый к Apache через PHP-FPM, и бесплатный сертификат Let's Encrypt.
Установка выполняется с помощью четырёх команд apt. Почти всё в этом руководстве посвящено настройке взаимодействия компонентов и небольшому набору ошибок, из-за которых новый стек может показывать пустую страницу, отдавать исходный код браузеру для скачивания или не позволять войти в только что установленную базу данных. У каждой такой ошибки есть характерный признак. Ниже для каждой ошибки указано точное сообщение, которое вы увидите.
Предварительные требования и важные ограничения
Предполагается чистый KVM VPS с Ubuntu 24.04, пользователем с правами sudo или учетной записью root и общедоступным IPv4-адресом. Минимальный стек работает при 1 GB оперативной памяти. Перед размещением реального приложения, использующего базу данных, выделите 2 GB, поскольку стандартные буферы MariaDB и несколько рабочих процессов PHP-FPM быстро занимают первый гигабайт.
До запуска Certbot в конце должны выполняться два условия. Подготовьте их заранее. Нужен домен с A-записью, указывающей на общедоступный IP-адрес VPS. Let's Encrypt проверяет домен по HTTP, поэтому для обычного IP-адреса сертификат получить нельзя. Кроме того, порты 80 и 443 должны быть доступны из интернета. У многих провайдеров это означает, что их нужно открыть в сетевом брандмауэре панели управления и в ufw на сервере. Изменения DNS могут распространяться до часа. Поэтому сначала создайте A-запись, и к моменту ее использования она уже будет действовать.
Шаг 1 — Установите Apache и проверьте страницу по умолчанию
sudo apt update
sudo apt install -y apache2apt запускает службу и настраивает ее автоматический запуск. Проверьте состояние:
systemctl status apache2Вы должны увидеть строку active (running). Теперь откройте http://YOUR_SERVER_IP/ в браузере. Страница Apache2 Ubuntu Default Page с крупным баннером «It works!» — это правильный результат. Она подтверждает, что Apache обслуживает запросы, а не свидетельствует об ошибке. Эта страница находится в /var/www/html/index.html, а обслуживается стандартным виртуальным хостом 000-default.conf, поставляемым вместе с пакетом. Позже вы отключите оба компонента. Сейчас их наличие — именно то, что нужно проверить.
Если страница вообще не загружается, но systemctl сообщает, что процесс запущен, доступ блокирует межсетевой экран. Это будет следующим шагом.
Шаг 2 — Откройте в брандмауэре доступ для HTTP и HTTPS
Пакет apache2 регистрирует три профиля приложений ufw. Выведите их список:
sudo ufw app listВы увидите Apache, Apache Full и Apache Secure. Профиль Apache разрешает только порт 80, Apache Secure — только порт 443, а Apache Full — оба порта. Нужен именно последний профиль, поскольку в конце вы добавите TLS.
sudo ufw allow OpenSSH
sudo ufw allow "Apache Full"
sudo ufw enableРазрешите OpenSSH до запуска ufw enable. По умолчанию ufw запрещает весь входящий трафик. Если включить его без правила для SSH, после активации вы потеряете собственное подключение. Текущий сеанс сохранится, но повторно подключиться вы уже не сможете. Проверьте результат с помощью sudo ufw status. Для OpenSSH, Apache Full и соответствующих правил для v6 должно отображаться значение ALLOW.
Шаг 3 — Установите MariaDB и защитите его
sudo apt install -y mariadb-server
systemctl status mariadbВ Ubuntu 24.04 поставляется MariaDB 10.11 — версия с долгосрочной поддержкой, поэтому внешний репозиторий не требуется. После запуска службы настройте ее безопасность:
sudo mysql_secure_installationВнимательно читайте запросы, а не нажимайте Enter без разбора. Когда появится запрос текущего пароля root, нажмите Enter: пароль еще не задан. На вопрос «Переключиться на аутентификацию unix_socket?» ответ ничего не изменит, поскольку для этого пакета она уже включена, поэтому нажмите n. Ответьте n на вопрос «Изменить пароль root?» по причине, указанной в следующем абзаце, а на остальные вопросы ответьте Y: удалите анонимных пользователей, запретите удаленный вход root, удалите тестовую базу данных и перезагрузите таблицы привилегий.
Ниже описана часть, которая часто вызывает вопросы. В MariaDB на Ubuntu учетная запись базы данных root использует аутентификацию unix_socket, а не пароль. Это означает, что база данных доверяет пользователю операционной системы, от имени которого уже выполнена аутентификация. Поэтому из оболочки root работает следующая команда:
sudo mysql...после чего открывается приглашение MariaDB [(none)]> без запроса пароля. Та же команда, выполненная непривилегированным пользователем, отклоняется. В этом и заключается смысл: доступ к root базы данных привязан к sudo на сервере, а пароль невозможно украсть, выманить или подобрать перебором. Это безопаснее пароля, поэтому оставьте эту настройку без изменений. Из этого следует правило: никогда не подключайте приложение к учетной записи root. Создайте отдельного пользователя для каждого приложения (шаг 7), поскольку приложение, подключающееся по TCP с именем пользователя и паролем, все равно не может использовать аутентификацию через сокет. Кроме того, каждое приложение должно иметь доступ только к собственной базе данных.
Шаг 4 — Установка PHP 8.3 с PHP-FPM
В Ubuntu 24.04 по умолчанию используется PHP 8.3. Установите диспетчер процессов FPM и расширения, которые обычно требуются приложению:
sudo apt install -y php8.3-fpm php8.3-mysql php8.3-cli \
php8.3-curl php8.3-xml php8.3-mbstring php8.3-zipОбратите внимание, чего нет в этом списке: libapache2-mod-php. Этот устаревший пакет встраивает интерпретатор PHP в каждый процесс Apache. Это простая схема, но каждый рабочий процесс содержит копию PHP независимо от того, обрабатывает ли он скрипт или статическое изображение. Их жизненные циклы связаны, и такая схема работает только с MPM prefork в Apache — наименее эффективным вариантом. PHP-FPM, напротив, запускает PHP в собственном пуле процессов, с которым Apache взаимодействует через сокет. Поэтому Apache может использовать многопоточный MPM event для статических файлов и передавать пулу только PHP-запросы. Пул можно настраивать независимо от веб-сервера. Та же конфигурация FPM будет работать и позже, если перед Apache будет установлен nginx. Это обоснованно считается текущим вариантом по умолчанию.
Apache взаимодействует с FPM через модуль proxy_fcgi. Включите его, включите конфигурацию, установленную пакетом FPM, и перезапустите Apache:
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.3-fpm
sudo systemctl restart apache2a2enconf php8.3-fpm активирует /etc/apache2/conf-available/php8.3-fpm.conf. Этот файл содержит правило, направляющее PHP-файлы к сокету FPM. Правило обрабатывает любой файл .php и передаёт его сокету по адресу /run/php/php8.3-fpm.sock:
<FilesMatch ".+\.ph(ar|p|tml)$">
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>Не редактируйте этот файл: он уже поставляется с корректными настройками. Однако знание пути к сокету помогает диагностировать ошибки «PHP скачивается вместо выполнения» и «Основной скрипт неизвестен», которые могут возникнуть позже. Обе ошибки связаны с тем, что Apache и FPM используют разные пути к этому сокету или к файлу, расположенному за ним.
Шаг 5 — Виртуальный хост приложения по имени
Виртуальный хостинг по имени позволяет обслуживать несколько сайтов с одного IP-адреса. Apache выбирает сайт по заголовку Host: в запросе. Создайте каталог приложения отдельно от стандартного /var/www/html:
sudo mkdir -p /var/www/testapp
sudo chown -R www-data:www-data /var/www/testapp
sudo chmod -R 755 /var/www/testappПрава владельца важны. В Ubuntu Apache и PHP-FPM работают от имени пользователя www-data. Поэтому файлы, которые должен читать веб-сервер, и каталоги, в которые приложение должно выполнять запись, например каталог для загрузок, должны принадлежать www-data. Если вы также будете редактировать файлы от имени своего пользователя, распространённый вариант — сделать себя владельцем файлов и добавить свою учётную запись в группу www-data. Для обычного развёртывания наименее неожиданным вариантом будет www-data:www-data.
Создайте конфигурацию виртуального хоста в /etc/apache2/sites-available/testapp.conf:
<VirtualHost *:80>
ServerName app.example.com
DocumentRoot /var/www/testapp
<Directory /var/www/testapp>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/testapp-error.log
CustomLog ${APACHE_LOG_DIR}/testapp-access.log combined
</VirtualHost>Укажите в ServerName настоящий домен. Директива Options -Indexes запрещает Apache показывать содержимое каталога, если в нём нет индексного файла. Иначе посетители смогут просматривать дерево исходного кода. Директива AllowOverride All позволяет использовать файл .htaccess. Это требуется большинству PHP-приложений для красивых URL. Если приложение не использует эту возможность, замените значение на None, чтобы немного повысить скорость. Включите этот сайт, отключите стандартный, проверьте конфигурацию и перезагрузите Apache:
sudo a2ensite testapp
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2Команда apache2ctl configtest должна вывести Syntax OK. Строку a2dissite 000-default часто забывают добавить. Поэтому позже может отображаться стандартная страница. Эта проблема описана в разделе о сбоях.
Шаг 6 - Убедитесь, что PHP работает, затем удалите файл проверки
Создайте однострочный PHP-файл в корневом каталоге приложения:
echo "<?php phpinfo();" | sudo tee /var/www/testapp/info.phpОткройте http://app.example.com/info.php. При правильном результате отобразится длинная фиолетово-серая таблица PHP Version 8.3.x со списком загруженных модулей. В строке Server API должно быть указано FPM/FastCGI. Эта последняя строка подтверждает, что запросы проходят через PHP-FPM, а не через mod_php.
Сразу удалите этот файл:
sudo rm /var/www/testapp/info.phpphpinfo() раскрывает точную версию PHP, все загруженные расширения, пути к файлам и сведения об окружении. Эти данные могут использовать злоумышленники, которые проверяют сервер на наличие версии с известной уязвимостью. Это проверка, а не функциональная возможность. Удалите файл сразу после просмотра страницы. Если вместо таблицы браузер предлагает скачать info.php, PHP не подключен к Apache. Прежде чем делать что-либо еще, перейдите к разделу об ошибках.
Шаг 7 — Создайте базу данных приложения и пользователя с минимально необходимыми правами
Откройте базу данных от имени root, прошедшего аутентификацию через сокет:
sudo mysqlЗатем создайте одну базу данных и одного пользователя, предоставив ему доступ только к этой базе данных:
CREATE DATABASE appdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'a-long-random-password';
GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;Здесь сделаны три осознанных выбора. utf8mb4 — это полноценный четырехбайтовый UTF-8, а устаревший псевдоним utf8 молча обрезает emoji и некоторые символы CJK, поэтому всегда используйте utf8mb4. Разрешение предоставляется для appdb.*, а не для *.*: этот пользователь может обращаться только к собственной базе данных и ни к чему другому. Поэтому уязвимость SQL-инъекции в приложении не позволит прочитать таблицы всех остальных сайтов. Кроме того, 'appuser'@'localhost' ограничивает учетную запись подключениями, инициированными на этом сервере.
Проверьте подключение от имени этого пользователя:
mysql -u appuser -p appdbКоманда запросит пароль и откроет приглашение MariaDB [appdb]>. Обратите внимание: флаг -h отсутствует. Не добавляйте его: клиент подключится через локальный Unix-сокет, и именно это MariaDB считает localhost. Важно знать: для MySQL и MariaDB localhost означает Unix-сокет, а 127.0.0.1 — TCP-подключение. В стандартной конфигурации MariaDB в Ubuntu 24.04 сервер по-прежнему преобразует TCP-подключение от 127.0.0.1 в localhost, поэтому оба варианта соответствуют учетной записи. Однако на серверах с включенным skip-name-resolve (это распространенная оптимизация производительности и стандартная настройка во многих образах контейнеров) эти два варианта сопоставляются с разными хостами. Поэтому приложение, подключающееся к 127.0.0.1, получает отказ с ошибкой ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' (using password: YES) даже при правильном пароле.
Поэтому укажите в приложении хост localhost, пользователя appuser и базу данных appdb, но никогда не используйте root. PHP's mysqli и PDO переключаются на Unix-сокет, если хостом является строка localhost, что соответствует созданной учетной записи. Если фреймворк требует числовой TCP-хост, создайте пользователя в соответствии с фактическим способом подключения: 'appuser'@'127.0.0.1' или @'%' (в сочетании с правилом межсетевого экрана) — только если базе данных необходимо принимать подключения с другого компьютера.
Шаг 8 — Добавление HTTPS с помощью Certbot
Передача формы входа через обычный HTTP отправляет пароли в открытом виде. Современные браузеры помечают такую страницу как «Небезопасно». Certbot устраняет эту проблему одной командой. Установите его вместе с плагином Apache:
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apacheЗдесь Certbot использует два плагина. Аутентификатор apache подтверждает, что вы управляете доменом: он временно размещает файл проверки через работающий Apache. Затем установщик apache изменяет конфигурацию виртуального хоста, добавляет блок 443, указывает его на новый сертификат и по умолчанию перенаправляет весь HTTP-трафик на HTTPS. Начиная с Certbot 2.0, вопрос о перенаправлении не задается. Передайте --no-redirect, если необходимо продолжить обслуживание обычного HTTP. Поскольку на шаге 5 вы указали настоящий ServerName, Certbot автоматически обнаружит домен. Сертификаты действуют 90 дней. Пакет устанавливает таймер systemd для их продления. Проверьте таймер с помощью sudo certbot renew --dry-run. Вывод должен заканчиваться на Congratulations, all simulated renewals succeeded.
Полное описание проверки, таймера продления, а также требований к DNS и брандмауэру приведено в сопутствующем руководстве выдача бесплатных TLS-сертификатов Let's Encrypt с Certbot в Apache.
Резервное копирование, обновление и усиление защиты
Создайте резервные копии двух компонентов, в которых хранится состояние системы: баз данных и корня веб-сайта. Ночной логический дамп — самый простой надежный подход, sudo sh -c 'mysqldump --all-databases --single-transaction | gzip > /root/db-$(date +%F).sql.gz', после чего его нужно скопировать за пределы сервера. Объединение всего конвейера в sudo sh -c важно: без этого оболочка выполняет перенаправление > /root/... от имени вашего пользователя и завершается ошибкой Permission denied, поскольку только mysqldump унаследовал sudo. --single-transaction создает согласованный снимок таблиц InnoDB без их блокировки. Добавьте к нему tar для /var/www и /etc/apache2/sites-available — и сможете восстановить весь стек на новом VPS из этих файлов.
Обновления — это обычная sudo apt update && sudo apt upgrade. Проблема возникает при обновлении версии PHP: когда будущая версия Ubuntu изменит версию PHP по умолчанию на PHP 8.4, apt может установить php8.4-fpm вместе с 8.3, сокет станет /run/php/php8.4-fpm.sock, а конфигурация Apache по-прежнему будет указывать на сокет версии 8.3. Включите новую конфигурацию (sudo a2enconf php8.4-fpm) и отключите старую. Иначе после обычного обновления сайт начнет возвращать Primary script unknown. Версии PHP выпускаются быстрее, чем обновляются дистрибутивы LTS, поэтому проверяйте актуальные примечания к выпуску PHP, а не фиксируйте версию исправления.
В первый день стоит выполнить два шага по усилению защиты. Во-первых, настройте наблюдение Fail2Ban за SSH на сервере: общедоступный VPS получает автоматические попытки входа через несколько минут, а небольшая jail превращает тысячи попыток в несколько попыток до блокировки. Во-вторых, если вы предпочитаете управлять виртуальными хостами Apache, базами данных MariaDB и пользователями через браузер, а не редактировать файлы вручную, веб-панель управления Webmin работает поверх этого стека и изменяет те же файлы конфигурации, которые вы только что создали. Ни один из этих инструментов не заменяет понимание компонентов, но оба упрощают повседневную работу.
Типичные ошибки и сообщения, которые вы увидите
Стандартная страница не исчезает. Вы изменили настройки виртуального хоста и перезагрузили конфигурацию, но браузер по-прежнему показывает «Apache2 Ubuntu Default Page» и баннер «It works!». Apache обслуживает первый подходящий виртуальный хост. Если ни один ServerName не соответствует запросу, используется конфигурация, которая идет первой в алфавитном порядке: 000-default.conf располагается перед testapp.conf. Имя хоста в запросе может не соответствовать вашему ServerName, либо вы не выполнили sudo a2dissite 000-default. Отключите стандартный виртуальный хост с помощью sudo systemctl reload apache2 и проверьте результат командой apache2ctl -S. Она выводит карту виртуальных хостов и показывает, какая конфигурация используется по умолчанию. Также очистите кэш браузера: сохраненный ответ 200 со старой страницы может продолжать отображаться.
Файл .php скачивается вместо выполнения. Вы открываете info.php, а браузер скачивает файл с исходным кодом <?php или показывает его как обычный текст вместо выполнения. Apache обслуживает файл как статический ресурс, потому что обработчик PHP не подключен, не выполнена команда sudo a2enmod proxy_fcgi или sudo a2enconf php8.3-fpm, либо после этого не был перезапущен Apache. Выполните все три действия (шаг 4) и повторите запрос. Проверьте загрузку модуля командой apache2ctl -M | grep fcgi. В ее выводе должен присутствовать proxy_fcgi_module. Это утечка исходного кода, а не косметическая ошибка. Исправьте ее до размещения на сервере рабочих данных.
ERROR 1698 (28000): Access denied for user 'root'@'localhost'. Вы выполнили mysql -u root или mariadb -u root без sudo. Учетная запись root использует аутентификацию через unix_socket. Поэтому войти можно только от имени ОС-пользователя root. Для исправления выполните sudo mysql: параметр -u root и пароль не требуются. Это ожидаемое поведение корректно работающей аутентификации через сокет, а не признак неисправной установки.
ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' из приложения при правильном пароле. Учетная запись существует как 'appuser'@'localhost', но приложение подключается по TCP к 127.0.0.1 на сервере, где отключено разрешение имен хостов (skip-name-resolve). Поэтому MariaDB воспринимает эти варианты как разные хосты: localhost — это Unix-сокет, а 127.0.0.1 — TCP. Укажите приложению хост localhost, чтобы оно использовало сокет и соответствовало учетной записи, либо создайте вторую учетную запись 'appuser'@'127.0.0.1', если фреймворк поддерживает только TCP.
AH01071: Got error 'Primary script unknown' в /var/log/apache2/testapp-error.log, при этом браузер показывает File not found.. Apache передал запрос PHP-FPM, но FPM не смог найти скрипт по пути, который ему передал Apache. Обычно причина одна из двух. Сокет FPM в конфигурации указывает на неустановленную версию PHP: например, после обновления остался сокет php8.4, а работает только версия 8.3. Либо файла действительно нет, потому что DocumentRoot и фактический каталог не совпадают. Проверьте наличие сокета командой ls -l /run/php/. Убедитесь, что DocumentRoot соответствует каталогу, где находится файл. Затем перезапустите php8.3-fpm и apache2.
AH00558: apache2: Could not reliably determine the server's fully qualified domain name при каждом перезапуске. Это безвредное предупреждение, а не ошибка. Apache сообщает, что глобальный параметр ServerName не задан. Чтобы убрать предупреждение, запишите ServerName your.domain в /etc/apache2/conf-available/servername.conf и выполните sudo a2enconf servername.
(98)Address already in use: AH00072: make_sock: could not bind to address 0.0.0.0:80 при запуске Apache. Другой веб-сервер уже использует порт 80. Часто это оставшийся после предыдущего эксперимента nginx. Найдите его командой sudo ss -ltnp | grep :80, затем остановите и отключите другой сервис перед запуском Apache.
FAQ
mod_php или PHP-FPM — что выбрать?
Используйте PHP-FPM. mod_php встраивает интерпретатор в каждый процесс Apache и требует медленного MPM prefork, поэтому Apache несет накладные расходы PHP даже при выдаче статического изображения. PHP-FPM запускает PHP в отдельном пуле с независимой настройкой, к которому Apache обращается через сокет. Он работает с более быстрым многопоточным MPM event и без изменений переносится на nginx в дальнейшем. Это современный вариант по умолчанию. mod_php имеет смысл только для устаревшего приложения, зависящего от поведения PHP внутри процесса.
Почему браузер скачивает файл PHP вместо его выполнения?
Apache обрабатывает файл .php как статический файл для скачивания, поскольку для него не назначен обработчик PHP. В Ubuntu 24.04 с FPM это означает, что вы пропустили один из шагов: sudo a2enmod proxy_fcgi, sudo a2enconf php8.3-fpm или последующий перезапуск Apache. Выполните все три команды и перезагрузите страницу. Затем с помощью apache2ctl -M | grep fcgi проверьте, что в списке присутствует proxy_fcgi_module. Пока вы не исправите эту проблему, сервер раскрывает исходный код. Считайте ее критической.
Почему доступ root в MariaDB запрещен даже при правильном пароле?
Потому что пароль не используется: MariaDB в Ubuntu аутентифицирует учетную запись root через unix_socket и связывает ее с пользователем root операционной системы. Команда mysql -u root из обычной оболочки по этой причине возвращает ERROR 1698 (28000): Access denied for user 'root'@'localhost'. Вместо нее подключайтесь с помощью sudo mysql. Для любого приложения создайте отдельного пользователя с аутентификацией по паролю, а не используйте root повторно.
Как добавить HTTPS на сайт LAMP?
Установите certbot и python3-certbot-apache, укажите в A-записи домена адрес сервера, затем выполните sudo certbot --apache. Аутентификатор Apache подтверждает контроль над доменом через работающий Apache. Установщик изменяет конфигурацию виртуального хоста для порта 443 и настраивает автоматическое продление сертификата. В полном руководстве по Certbot и Apache описаны проверка владения доменом, таймер продления и распространенные причины сбоев.