Laravel — практичный PHP-фреймворк для небольших команд: маршрутизация, ORM, очереди, миграции и структура. Но между php artisan serve на ноутбуке и сайтом на VPS лежит настройка PHP-FPM, Nginx, базы, прав и деплоя.
Что понадобится
Возьмите Linux-сервер с актуальной веткой PHP, Composer 2, MySQL или PostgreSQL, Nginx и Redis для кэша. Для небольшого проекта хватит 2 ГБ RAM. Домен направьте заранее, чтобы проверить доступность.
- PHP с расширениями
pdo_mysql,mbstring,xml,curl,zipиopcache. - Composer 2, Git, Nginx и PHP-FPM одной версии с CLI.
- Отдельный пользователь MySQL с правами только на базу проекта.
Установка PHP, Composer и проекта
В Debian и Ubuntu PHP ставится из штатного репозитория; более новую версию берите из репозитория производителя. После установки проверьте, что CLI и FPM видят одни и те же расширения.
sudo apt update
sudo apt install php php-fpm php-mysql php-xml php-mbstring php-curl php-zip unzip nginx composer
php -v
php -m | grep -E 'pdo_mysql|mbstring|opcache'
Создайте проект в каталоге сайта. composer create-project скачает фреймворк и зависимости; на рабочем сервере затем используйте --no-dev.
cd /var/www
sudo composer create-project laravel/laravel app
cd /var/www/app
php artisan --version
php artisan key:generate
База данных и переменные окружения
Создайте базу и отдельного пользователя. Не используйте root в .env и не выдавайте права на все базы. Пароль храните в менеджере секретов.
sudo mysql -u root -p
CREATE DATABASE app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'app'@'127.0.0.1' IDENTIFIED BY 'CHANGE_ME';
GRANT ALL PRIVILEGES ON app.* TO 'app'@'127.0.0.1';
FLUSH PRIVILEGES;
В .env укажите production и выключите отладку. APP_DEBUG=true показывает stack trace и переменные окружения — прямой путь к утечке.
APP_NAME=Example
APP_ENV=production
APP_DEBUG=false
APP_URL=https://example.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=app
DB_USERNAME=app
DB_PASSWORD=CHANGE_ME
Выполните миграции и создайте ссылку на хранилище. В production запускайте их с --force, иначе Laravel спросит подтверждение.
php artisan migrate --force
php artisan storage:link
php artisan db:show
php artisan about
Маршруты, контроллер и проверка
Добавьте health-check и endpoint с валидацией, чтобы проверить не только страницу, но и базу. Валидацию держите в Form Request или контроллере.
use Illuminate\Support\Facades\Route;
Route::get('/health', fn () => response()->json(['status' => 'ok']));
Route::post('/orders', [OrderController::class, 'store'])
->middleware('throttle:30,1');
Модель и миграция создаются одной командой. Для денег используйте decimal, а не float; внешние ключи задавайте на уровне БД, если они не мешают шардированию.
php artisan make:model Order -m
php artisan make:controller OrderController
php artisan route:list
php artisan test
Nginx и PHP-FPM
Корень сайта должен указывать на public, а не на корень проекта: иначе .env и исходники доступны по HTTP. Конфигурация ниже использует сокет PHP-FPM; его имя зависит от версии, сверьтесь с /run/php/.
server {
listen 80;
server_name example.com;
root /var/www/app/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
TLS завершайте на Nginx или балансировщике. Проверьте конфигурацию и перезагрузите службы. Если Nginx отдаёт 502, обычно виноват путь к сокету или упавший PHP-FPM; nginx -t и systemctl status покажут причину.
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl status php8.3-fpm --no-pager
curl -I http://example.com/health
Права, кэш и оптимизация
Запись нужна только в storage и bootstrap/cache. Не делайте chmod -R 777: это маскирует проблему и снижает безопасность. Владелец — пользователь PHP-FPM, обычно www-data.
sudo chown -R www-data:www-data /var/www/app/storage /var/www/app/bootstrap/cache
sudo find /var/www/app/storage -type d -exec chmod 775 {} \;
sudo find /var/www/app/storage -type f -exec chmod 664 {} \;
sudo chown www-data:www-data /var/www/app/.env
sudo chmod 640 /var/www/app/.env
В production включите кэш конфигурации, маршрутов и представлений. После деплоя его пересобирают, иначе приложение работает со старыми настройками. Для отладки есть php artisan optimize:clear.
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan optimize
Очереди и планировщик
Долгие задачи — письма, отчёты, обработка изображений — выносите в очередь. Воркер запускайте через systemd: он перезапускается после падения и стартует с системой. Не держите его в одной сессии с деплоем.
[Unit]
Description=Laravel queue worker
After=network.target mysql.service
[Service]
User=www-data
Group=www-data
Restart=always
WorkingDirectory=/var/www/app
ExecStart=/usr/bin/php /var/www/app/artisan queue:work --sleep=1 --tries=3 --max-time=3600
[Install]
WantedBy=multi-user.target
Планировщику нужна одна cron-запись, а не десятки задач. Она запускает schedule:run каждую минуту, а фреймворк решает, что выполнять.
* * * * * cd /var/www/app && php artisan schedule:run >> /dev/null 2>&1
sudo systemctl daemon-reload
sudo systemctl enable --now laravel-worker
systemctl status laravel-worker --no-pager
Деплой без сюрпризов
Деплой — фиксированная последовательность: режим обслуживания, обновление кода, зависимости без dev-пакетов, миграции, пересборка кэша и возврат сайта. Каждый шаг должен останавливать процесс при ошибке.
cd /var/www/app
php artisan down --retry=60
git pull --ff-only
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan queue:restart
php artisan up
Храните релизы в каталогах и переключайте ссылку current для отката. Для небольшого проекта достаточно git и резервной копии базы, но backup проверяйте восстановлением.
Диагностика
- 500 после деплоя: смотрите
storage/logs/laravel.log, права наstorageиAPP_KEY. - 502 от Nginx: проверьте сокет PHP-FPM,
systemctl statusиnginx -t. - Старые настройки: выполните
php artisan config:clearи пересоберите кэш. - Очередь стоит: проверьте
systemctl status laravel-workerиphp artisan queue:failed. - 419 при отправке формы: проверьте драйвер сессий, домен и secure-cookie.
tail -n 100 /var/www/app/storage/logs/laravel.log
php artisan about
php artisan queue:failed
sudo journalctl -u php8.3-fpm -n 50 --no-pager
Чек-лист
APP_ENV=productionиAPP_DEBUG=false.- Корень Nginx указывает на
public, доступ к.envзакрыт. - Права на
storageиbootstrap/cacheминимально достаточные. - Очередь и планировщик запущены через systemd и cron.
Итог
Laravel удобен, когда инфраструктура настроена предсказуемо: PHP-FPM и Nginx отдают только public, база доступна отдельному пользователю, а деплой состоит из повторяемых шагов. Начните с health-check и миграций, затем добавьте очереди и кэш. Не отключайте отладку «на время», не давайте права 777 и не храните секреты в git — эти привычки закрывают большинство проблем до первого инцидента.