Laravel в production: чек-лист перед запуском на VPS
Практический чек-лист развёртывания Laravel на VPS — от APP_DEBUG и кэширования конфигурации до очередей через supervisor, прав на storage и корректного планировщика задач.
Окружение: APP_ENV, APP_DEBUG и защита .env
Первое, что стоит проверить перед публикацией Laravel-приложения на VPS, — значения APP_ENV и APP_DEBUG в файле .env. APP_ENV должен быть выставлен в production, а APP_DEBUG — строго в false. Включённый режим отладки в production — одна из самых частых и опасных ошибок конфигурации: любое необработанное исключение покажет посетителю не аккуратную страницу ошибки, а полную диагностическую страницу Laravel с трассировкой стека вызовов, путями к файлам на сервере, значениями переменных окружения и фрагментами исходного кода.
# .env — обязательные значения для production
APP_ENV=production
APP_DEBUG=false
Сам файл .env, содержащий пароли от базы данных, ключи сторонних API и секретный ключ приложения APP_KEY, ни при каких обстоятельствах не должен быть доступен через веб-сервер напрямую и не должен попадать в систему контроля версий — стандартная конфигурация nginx для Laravel указывает корневым каталогом именно подкаталог public, что автоматически исключает прямой доступ к .env, лежащему уровнем выше, но эту настройку стоит явно проверить, а не считать само собой разумеющейся, особенно после ручного изменения конфигурации виртуального хоста.
server {
root /var/www/myapp/public;
location ~ /\.(?!well-known).* {
deny all;
}
}
Кэширование конфигурации, роутов и представлений
В режиме разработки Laravel читает конфигурацию, файлы роутов и шаблоны Blade заново при каждом запросе, что удобно во время активной разработки, но создаёт лишние накладные расходы на production, где эти данные между деплоями не меняются. Artisan предоставляет набор команд кэширования, собирающих каждую из этих категорий в единый оптимизированный файл.
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
Ключевой практический нюанс, часто становящийся источником путаницы: после выполнения config:cache Laravel перестаёт обращаться к файлу .env напрямую и берёт все настройки исключительно из собранного файла кэша bootstrap/cache/config.php — если после этого внести изменение в .env и не выполнить config:cache заново, изменение попросту не подействует, и разработчик может потратить немало времени, пытаясь понять, почему «настройка не применяется», хотя файл .env отредактирован верно.
Все команды кэширования нужно перевыполнять при каждом деплое новой версии кода, после любого изменения .env, маршрутов или конфигурации — в реальных production-пайплайнах это обычно оформляется отдельным шагом в скрипте деплоя сразу после установки зависимостей, а не выполняется вручную от случая к случаю.
php artisan config:clear
php artisan route:clear
php artisan view:clear
php artisan cache:clear
Очереди в production: воркеры под управлением supervisor
Фоновые задачи (отправка писем, обработка загруженных файлов, интеграции со сторонними API) в Laravel обычно выносятся в очередь, обрабатываемую отдельным процессом-воркером через команду php artisan queue:work. Запуск этого процесса вручную в фоновом режиме через & или nohup работает для разовой проверки, но абсолютно не подходит для production: такой процесс не перезапустится ни при падении из-за необработанного исключения, ни при перезагрузке сервера, и его состояние никак не отслеживается системой.
# /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/myapp/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/myapp/storage/logs/worker.log
Supervisor берёт на себя автоматический запуск указанного числа воркеров (numprocs), их перезапуск при падении, и упорядоченное логирование вывода в отдельный файл. Флаг --tries ограничивает число повторных попыток выполнения упавшей задачи перед переносом её в таблицу неудачных задач (failed_jobs), а --max-time периодически перезапускает воркер целиком — это защищает от постепенного накопления утечек памяти в длительно работающем PHP-процессе, что особенно актуально для воркеров, обрабатывающих задачи часами без перезапуска.
supervisorctl reread
supervisorctl update
supervisorctl start laravel-worker:*
После деплоя новой версии кода воркеры обязательно нужно перезапустить командой php artisan queue:restart — она не убивает процессы принудительно, а выставляет специальный флаг, после проверки которого каждый воркер корректно завершает уже начатую задачу и останавливается сам, а supervisor, обнаружив остановку процесса, автоматически поднимает новый воркер, который при старте загрузит уже обновлённый код приложения.
Права доступа на storage и bootstrap/cache, символические ссылки
Laravel записывает логи, кэш представлений, файлы сессий и загруженные пользователями файлы в каталог storage, а также кэширует часть служебных данных в bootstrap/cache — оба каталога должны быть доступны на запись пользователю, от имени которого работает PHP-FPM (обычно www-data), иначе приложение будет выдавать ошибки прав доступа при попытке что-либо туда записать.
chown -R www-data:www-data storage bootstrap/cache
find storage bootstrap/cache -type d -exec chmod 775 {} \;
find storage bootstrap/cache -type f -exec chmod 664 {} \;
Отдельная типичная задача — символическая ссылка storage/app/public, ведущая из публично доступного каталога public/storage к реальному месту хранения загруженных пользователями файлов внутри storage. Без этой ссылки файлы, загруженные через Storage::disk('public'), физически сохраняются на диске, но остаются недоступны напрямую по URL, что на практике проявляется как «изображения загружаются, но не отображаются на сайте».
php artisan storage:link
Эту команду нужно выполнять один раз при первом развёртывании на новом сервере — при переносе кода на новый VPS или при клонировании проекта заново символическая ссылка не переносится автоматически вместе с файлами (в зависимости от способа переноса) и её отсутствие после миграции — частая причина внезапно «пропавших» изображений сразу после переезда на новый сервер.
Планировщик задач Laravel и системный cron
Laravel предоставляет собственный, удобный способ описания периодических задач прямо в коде приложения (в методе schedule класса Kernel или, в более новых версиях, в файле routes/console.php) вместо необходимости заводить отдельную запись в системном cron для каждой отдельной задачи. Но сам механизм планировщика Laravel не работает сам по себе — ему всё равно требуется единственная, постоянно срабатывающая запись в системном cron, которая раз в минуту запускает команду schedule:run, а уже она сама решает, какие из описанных в коде задач должны выполниться именно в этот момент.
* * * * * cd /var/www/myapp && php artisan schedule:run >> /dev/null 2>&1
Частая ошибка — попытка напрямую прописать в системном cron каждую отдельную задачу приложения со своим расписанием, дублируя логику, которая уже описана в коде планировщика Laravel, — это удваивает место, где нужно поддерживать расписание, и создаёт риск рассинхронизации между тем, что реально настроено на сервере, и тем, что видно в коде приложения при его чтении.
Ещё один практический момент — задачи, описанные через withoutOverlapping(), защищают от повторного одновременного запуска одной и той же длительной задачи, если предыдущий запуск ещё не завершился к моменту следующего срабатывания по расписанию (например, ежеминутная задача, которая иногда выполняется дольше минуты) — эту защиту стоит явно добавлять для любой задачи, время выполнения которой не гарантированно укладывается в интервал между её запусками.
OPcache и оптимизация автозагрузки composer
PHP OPcache кэширует скомпилированный байт-код скриптов в разделяемой памяти между запросами, избавляя от необходимости заново разбирать и компилировать PHP-файлы при каждом запросе, — для production-окружения Laravel включённый и правильно настроенный OPcache даёт заметный прирост производительности буквально «бесплатно», без изменений в коде приложения.
; php.ini — рекомендуемые настройки OPcache для production
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
opcache.revalidate_freq=0
Параметр opcache.validate_timestamps=0 отключает проверку времени модификации файлов при каждом запросе, что даёт максимальную производительность, но означает, что PHP-FPM продолжит выполнять уже закэшированную в памяти версию кода, даже если файлы на диске были обновлены, — при такой настройке перезапуск (или явный сброс кэша через opcache_reset()) после каждого деплоя становится обязательным шагом, а не опциональной подстраховкой.
Помимо OPcache, установку зависимостей composer в production стоит выполнять с флагами, исключающими зависимости для разработки и заранее оптимизирующими автозагрузку классов.
composer install --no-dev --optimize-autoloader --no-interaction
Флаг --optimize-autoloader собирает статическую карту классов вместо динамического поиска файлов через PSR-4 на каждый запрос, что особенно заметно на приложениях с большим количеством классов, а --no-dev исключает пакеты из require-dev (тестовые фреймворки, инструменты отладки), сокращая объём разворачиваемого на сервере кода и убирая инструменты, которые ни при каких обстоятельствах не должны присутствовать в production-окружении.
Финальный чек-лист перед первым запуском
Собранный воедино порядок проверок перед тем, как открыть Laravel-приложение для реальных посетителей: APP_ENV=production и APP_DEBUG=false в .env; файл .env закрыт от прямого доступа через веб-сервер и не находится в системе контроля версий; выполнены config:cache, route:cache и view:cache; зависимости composer установлены с --no-dev --optimize-autoloader.
Далее: права на storage и bootstrap/cache выставлены на пользователя PHP-FPM; выполнена storage:link, если приложение использует публичное хранилище файлов; настроена единственная запись cron на schedule:run; воркеры очереди (если приложение их использует) запущены и управляются через supervisor, а не вручную в фоне; OPcache включён с настройками для production, и в процесс деплоя включён шаг перезапуска PHP-FPM или сброса OPcache.
Отдельно стоит убедиться, что миграции базы данных применены (php artisan migrate --force — флаг --force обязателен в production, поскольку без него Laravel по умолчанию запросит интерактивное подтверждение, что несовместимо с автоматизированным деплоем), а резервное копирование базы данных и файлов настроено и реально протестировано восстановлением до того, как приложение получит первых реальных пользователей, а не после первого инцидента.
Логирование ошибок и каналы уведомлений
Отключение APP_DEBUG убирает подробную диагностику из ответа, показываемого посетителю, но не должно означать, что администратор приложения остаётся вообще без информации об ошибках — Laravel продолжает записывать полную трассировку любого необработанного исключения в storage/logs/laravel.log независимо от значения APP_DEBUG, и этот файл — первое место, куда стоит смотреть при любой странной ошибке в production.
# config/logging.php — канал с ротацией логов вместо одного бесконечно растущего файла
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => 'error',
'days' => 14,
],
Канал daily вместо однофайлового stack предотвращает бесконтрольный рост единственного файла лога до размеров, мешающих его открыть и прочитать, и автоматически удаляет старые файлы по истечении заданного числа дней. Для production-окружения с реальными пользователями разумно дополнительно подключить внешний сервис агрегации и оповещения об ошибках (Sentry, Bugsnag или подобные) через готовый пакет интеграции — это даёт мгновенное уведомление о новой ошибке сразу после её первого появления, а не только при случайном просмотре файла лога кем-то из команды.
Важно также разграничивать уровень логирования по степени серьёзности — не каждое исключение требует немедленного оповещения дежурного администратора, и настройка минимального уровня (level => 'error') для внешнего канала уведомлений в сочетании с более подробным локальным логом всех событий вплоть до debug помогает избежать усталости от избыточных уведомлений при реальном сохранении полной картины на случай более глубокого расследования.
Обслуживание без простоя: maintenance mode и порядок деплоя
Любой деплой новой версии кода на работающий production-сервер — момент риска: файлы обновляются не мгновенно, и в промежутке между применением новых файлов и завершением сопутствующих шагов (миграции базы данных, пересборка кэша конфигурации) сайт может оказаться в противоречивом состоянии, где часть кода уже новая, а часть окружения — ещё старая.
php artisan down --secret="deploy-token-2026" --retry=30
# ... миграции, кэширование, обновление зависимостей ...
php artisan up
Команда artisan down показывает всем посетителям страницу обслуживания вместо приложения, а флаг --secret позволяет команде разработки продолжать проверять сайт по специальной ссылке в обход этой страницы прямо во время деплоя, до его официального завершения. Флаг --retry сообщает браузерам и прокси, через сколько секунд стоит повторить запрос, что предотвращает восприятие сайта как полностью упавшего внешними системами мониторинга во время короткого штатного обслуживания.
Для проектов, где даже кратковременная недоступность неприемлема, применяется схема с параллельным развёртыванием новой версии в отдельный каталог и атомарным переключением символической ссылки текущей версии на новый каталог только после того, как все подготовительные шаги (зависимости, кэш, миграции, требующие обратной совместимости) полностью завершены и проверены, — популярные инструменты деплоя для Laravel (Deployer и подобные) реализуют именно такую схему, сводя реальное окно недоступности практически к нулю.
Почему стоит выбрать нас
Быстрый запуск
Подберём конфигурацию и развернём проект в день обращения.
Надёжность
NVMe-диски, защита от DDoS и резервные копии по расписанию.
Поддержка 24/7
Дежурные инженеры на связи в любое время суток.
Настройка под ключ
Установим CMS, настроим веб-сервер, SSL и почту за вас.
Перенос без простоя
Аккуратно перенесём сайт и базу данных на новый сервер.
Прозрачные цены
Актуальные тарифы и честный подбор без навязывания.
Вопросы и ответы
Что будет, если забыть выключить APP_DEBUG в production?
При включённом APP_DEBUG любая необработанная ошибка покажет посетителю полную страницу с трассировкой стека, путями на диске сервера, содержимым переменных окружения и фрагментами исходного кода — это прямая утечка чувствительной информации, которую атакующий может использовать для дальнейшей разведки уязвимостей приложения.
Нужно ли каждый раз вручную выполнять php artisan config:cache после изменения .env?
Да, обязательно — если конфигурация закэширована через config:cache, Laravel перестаёт читать файл .env напрямую и берёт все значения из собранного кэша конфигурации, поэтому изменения в .env без пересборки кэша попросту не применятся, что становится частым источником путаницы при отладке production-окружения.
Почему очередь через supervisor лучше, чем просто запустить php artisan queue:work в фоне вручную?
Процесс, запущенный вручную через & или nohup, не перезапустится автоматически при падении, перезагрузке сервера или превышении лимита памяти воркера, и его состояние никак не отслеживается системой — supervisor берёт на себя автоматический перезапуск, ограничение числа одновременных воркеров и упорядоченное логирование, что необходимо для надёжной работы очередей в production.
Как безопасно перезапустить воркеры очереди после деплоя новой версии кода?
Командой php artisan queue:restart — она не убивает воркеры принудительно, а выставляет сигнальный флаг, после которого каждый воркер завершает уже начатую задачу и корректно останавливается, а supervisor, отслеживающий процесс, автоматически поднимает новый воркер, который загрузит уже обновлённый код.
Обязательно ли использовать --no-dev при установке зависимостей composer в production?
Настоятельно рекомендуется — зависимости из секции require-dev (тестовые фреймворки, отладочные инструменты вроде Laravel Debugbar) не нужны для работы приложения, увеличивают время установки и объём кода, разворачиваемого на сервере, а некоторые из них при случайном включении в production могут сами по себе создавать риски безопасности.
Что делать, если после деплоя сайт показывает ошибку 500 без подробностей?
Проверить файл storage/logs/laravel.log — Laravel продолжает подробно логировать ошибки туда независимо от значения APP_DEBUG, которое влияет только на то, что показывается непосредственно посетителю в браузере, а не на то, что записывается в лог для последующей диагностики администратором.
Нужно ли перезапускать PHP-FPM после каждого деплоя?
Не всегда обязательно, но необходимо при включённом OPcache без настроенной автоматической проверки изменений файлов (opcache.validate_timestamps=0) — в этом случае PHP-FPM продолжит выполнять закэшированную в памяти старую версию кода до перезапуска, даже если файлы на диске уже обновлены.
Как оплачивать услуги?
Оплата помесячная, для облачных серверов доступна почасовая тарификация. Принимаются карты, банковские переводы и электронные кошельки.
Есть ли круглосуточная поддержка?
Да, дежурные инженеры на связи 24/7.
Выдаёте ли закрывающие документы?
Да, предоставим необходимые документы для оплаты.
Как оставить заявку?
Заполните форму на странице или нажмите «Заказать» — мы свяжемся и поможем с подбором.