Встановлення LAMP на Ubuntu 24.04 з PHP-FPM
Налаштуйте Apache, MariaDB з unix_socket та PHP 8.3 через PHP-FPM на Ubuntu 24.04. Отримайте vhost та безкоштовний HTTPS через Certbot у цьому посібнику.
Що ви створюєте
LAMP stack складається з чотирьох компонентів на одному сервері Ubuntu 24.04: ядра Linux, Apache для обробки HTTP, MariaDB для зберігання даних та PHP 8.3 для виконання коду. У результаті ви отримаєте віртуальний хост на основі доменного імені, що обслуговує директорію додатка, базу даних з користувачем з мінімальними привілеями, PHP, інтегрований в Apache через PHP-FPM, та безкоштовний сертифікат Let's Encrypt.
Процес встановлення складається з чотирьох apt команд. Більша частина цього посібника присвячена налаштуванню зв'язків між компонентами та розв'язанню типових помилок. Через ці помилки нова інсталяція може відображати порожню сторінку, пропонувати завантажити вихідний код замість виконання або блокувати доступ до щойно встановленої бази даних. Кожна така помилка має характерні ознаки, які наведено нижче з точним текстом повідомлення.
Prerequisites and the honest gotchas
Припустимо, що у вас є чиста Ubuntu 24.04 KVM VPS з користувачем sudo або root та публічною IPv4 адресою. Мінімальний стек споживає 1 GB RAM; виділіть 2 GB перед встановленням додатка з повноцінною базою даних, оскільки стандартні буфери MariaDB та кілька процесів PHP-FPM швидко заповнюють перший гігабайт.
Для коректної роботи Certbot наприкінці мають бути виконані дві умови. Потрібно мати domain name з A-записом, що вказує на публічну IP адресу VPS — Let's Encrypt перевіряє домен через HTTP, а гола IP адреса не може отримати сертифікат. Також порти 80 та 443 мають бути доступні з інтернету; у багатьох провайдерів це означає відкриття портів як у мережевому firewall у панелі керування, так і в ufw на самому сервері. Зміни DNS можуть застосовуватися до одного години, тому спочатку налаштуйте A-запис, щоб він став активним до моменту використання.
Step 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 показує, що процес запущено, перешкодою є firewall. Це наступний крок.
Step 2 - Відкрийте firewall для 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.
Step 3 - Встановлення MariaDB та її захист
sudo apt install -y mariadb-server
systemctl status mariadbUbuntu 24.04 постачається з MariaDB 10.11 (версія з довгостроковою підтримкою), тому зовнішні репозиторії не потрібні. Після запуску служби виконайте її налаштування безпеки:
sudo mysql_secure_installationЗамість бездумного натискання Enter варто уважно прочитати запити. Коли з'явиться запит на current root password, натисніть Enter — пароля ще немає. На запит "Switch to unix_socket authentication?" відповідь не вплине, оскільки цей метод уже увімкнено для цього пакета, тому натисніть n. На запит "Change the root password?" виберіть n (причина вказана нижче), а на решту запитів відповідайте Y: видалити анонімних користувачів, заборонити віддалений вхід для root, видалити тестову базу даних та перезавантажити таблиці прав доступу.
Ось момент, який часто викликає плутанину. У MariaDB на Ubuntu обліковий запис бази даних root використовує автентифікацію unix_socket, а не пароль. Це означає, що база даних довіряє користувачу операційної системи, через якого ви вже пройшли автентифікацію. Тому це працює з оболонки root:
sudo mysql...і ви потрапляєте в командний рядок MariaDB [(none)]> без запиту пароля. Виконання тієї ж команди від імені непривілейованого користувача призведе до відмови, у чому і полягає суть: доступ до root бази даних прив'язаний до sudo у системі, тому пароля, який можна вкрасти, фішити або підібрати методом brute-force, не існує. Це безпечніше за пароль, тому не змінюйте ці налаштування. Висновок із цього: ніколи не використовуйте обліковий запис root для підключення застосунків. Створюйте окремого користувача для кожного застосунку (Step 7), оскільки застосунок, що підключається через TCP за допомогою імені користувача та пароля, не може використовувати socket auth, і кожен застосунок має мати доступ лише до своєї бази даних.
Step 4 - Встановлення PHP 8.3 з PHP-FPM
Версія PHP за замовчуванням в Ubuntu 24.04 — 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. Це простий метод, але кожен робочий процес (worker) несе копію PHP, незалежно від того, чи обробляє він скрипт, чи статичне зображення. Вони мають спільний життєвий цикл, і цей метод працює лише з Apache prefork MPM — найменш ефективним MPM. Натомість PHP-FPM запускає PHP як окремий пул процесів, з яким Apache взаємодіє через сокет. Apache може використовувати потоковий event MPM для статичних файлів і передавати лише PHP-запити. Пул налаштовується незалежно від вебсервера, і таку саму конфігурацію FPM можна використовувати пізніше, якщо встановити nginx попереду. Це стандарт за об'єктивними причинами.
Apache взаємодіє з FPM через модуль proxy_fcgi. Увімкніть його, увімкніть конфігурацію, яку встановив пакет FPM, і перезапустіть сервіс:
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 завантажується замість виконання" та "Primary script unknown". Обидві помилки виникають через невідповідність налаштувань сокета або файлів у Apache та FPM.
Step 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Права власності мають значення. Apache та PHP-FPM на Ubuntu працюють від імені користувача 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 відображати список файлів у директорії, якщо там немає index-файлу; без цього відвідувачі зможуть переглядати структуру вашого коду. Параметр AllowOverride All дозволяє працювати файлу .htaccess, який необхідний більшості PHP-додатків для використання зрозумілих URL; якщо ваш додаток його не потребує, видаліть його з None для невеликого приросту швидкості. Увімкніть цей сайт, вимкніть стандартний, перевірте конфігурацію та перезавантажте сервіс:
sudo a2ensite testapp
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2Команда apache2ctl configtest має вивести Syntax OK. Рядок a2dissite 000-default часто забувають, через що стандартна сторінка потім продовжує відображатися — це описано в розділі з помилками.
Step 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; перейдіть до розділу з переліком помилок, перш ніж продовжувати.
Step 7 - Створення бази даних додатка та користувача з мінімальними привілеями
Відкрийте базу даних під root-користувачем з автентифікацією через socket:
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-injection у додатку не дозволить прочитати таблиці інших сайтів. А 'appuser'@'localhost' обмежує обліковий запис підключеннями лише з цієї машини.
Перевірте доступ під цим користувачем:
mysql -u appuser -p appdbСистема запитає пароль і відкриє prompt MariaDB [appdb]>. Зверніть увагу, що прапор -h відсутній — якщо його не використовувати, клієнт підключиться через локальний Unix socket, що MariaDB вважає за localhost. Важливий нюанс: для MySQL та MariaDB localhost означає Unix socket, а 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), навіть якщо пароль правильний.
Тому вказуйте для додатка host localhost, user appuser, database appdb — ніколи не використовуйте root. PHP-розширення mysqli та PDO перемикаються на Unix socket, якщо host — це рядок localhost, що відповідає щойно створеному обліковому запису. Якщо фреймворк вимагає числовий TCP host, створюйте користувача відповідно до способу підключення: 'appuser'@'127.0.0.1', або @'%' (разом із правилом firewall), якщо доступ до бази необхідний з іншої машини.
Step 8 - Додавання HTTPS за допомогою Certbot
Передача форми логіна через HTTP передає паролі у відкритому вигляді. Сучасні браузери маркують такі сторінки як "Not secure". Certbot вирішує цю проблему однією командою. Встановіть його за допомогою плагіна для Apache:
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apacheCertbot використовує два плагіни. apache authenticator підтверджує контроль над доменом, тимчасово надаючи файл-виклик через запущений Apache. apache installer переписує конфігурацію virtual host, додає блок 443, вказує шлях до нового сертифіката та налаштовує перенаправлення з HTTP на HTTPS за замовчуванням. Починаючи з Certbot 2.0, запит про перенаправлення відсутній; використовуйте --no-redirect, якщо потрібно залишити роботу через HTTP. Оскільки на кроці 5 ви налаштували справжній ServerName, Certbot автоматично визначить домен. Сертифікати діють 90 днів. Пакет встановлює systemd timer для їх автоматичного оновлення; перевірте таймер за допомогою sudo certbot renew --dry-run, він має закінчуватися на Congratulations, all simulated renewals succeeded.
Для отримання повної інструкції щодо проходження перевірки, роботи таймера оновлення, а також вимог до DNS та firewall, перегляньте посібник issuing free Let's Encrypt TLS certificates with Certbot on Apache.
Backups, upgrades, and hardening
Зробіть резервні копії двох компонентів, що зберігають стан системи: баз даних та кореневої директорії веб-сайту (web root). Найпростіший надійний метод — щоденний логічний дамп sudo sh -c 'mysqldump --all-databases --single-transaction | gzip > /root/db-$(date +%F).sql.gz' з подальшим копіюванням на інший сервер. Важливо використовувати sudo sh -c для всього конвеєра: без нього оболонка (shell) виконує перенаправлення > /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 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 працює саме з цим стеком і керує тими самими конфігураційними файлами, які ви щойно створили. Жоден із цих інструментів не замінює розуміння компонентів системи, але обидва зменшують щоденні труднощі в адмініструванні.
Режими відмови та повідомлення, які ви побачите
Сторінка за замовчуванням не зникає. Ви відредагували virtual host, перезавантажили конфігурацію, але браузер все ще відображає "Apache2 Ubuntu Default Page" та банер "It works!". Apache віддає перший відповідний virtual host. Якщо жоден ServerName не відповідає запиту, перемагає перший за алфавітом конфіг — 000-default.conf сортується перед testapp.conf. Або ім'я хоста у запиті не збігається з вашим ServerName, або ви не виконали sudo a2dissite 000-default. Вимкніть стандартний конфіг, sudo systemctl reload apache2, і перевірте результат за допомогою apache2ctl -S; ця команда виведе карту vhost і покаже, який конфіг відповідає за сторінку за замовчуванням. Також очистіть кеш браузера; кеш із кодом 200 від старої сторінки може зберігатися.
Замість виконання .php файл завантажується. Ви відкриваєте info.php, але браузер завантажує файл із сирим кодом <?php або відображає його як звичайний текст замість виконання. Apache віддає файл як статичний ресурс, тому що PHP handler не підключено — ви пропустили sudo a2enmod proxy_fcgi, sudo a2enconf php8.3-fpm або не перезавантажили Apache. Виконайте всі три кроки (Step 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 auth, тому він приймає підключення лише тоді, коли ваш користувач ОС є root. Виправлення — sudo mysql: без -u root, без пароля. Це повідомлення є очікуваною поведінкою при коректній роботі socket auth, а не ознакою помилки встановлення.
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 socket, 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 і змушує використовувати повільний prefork MPM, тому Apache несе навантаження PHP навіть під час віддачі статичного зображення. PHP-FPM запускає PHP як окремий пул з незалежним налаштуванням, до якого Apache звертається через сокет; це дозволяє використовувати швидший потоковий event MPM і полегшує перехід на nginx у майбутньому. Це сучасний стандарт; mod_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, після чого інсталятор переписує virtual host для порту 443 та налаштовує автоматичне оновлення. Повний посібник з Certbot та Apache описує процес підтвердження, таймер оновлення та типові помилки.