Шаред-хостинг хочет быть всей вашей жизнью: панель cPanel, тикет в поддержку, прежде чем вы хоть что-то тронете в php.ini, и общий пул воркеров, который ложится под криптомайнер соседа. VPS даёт вам ту версию PHP, которую вы выбрали, базу данных не на одной машине с тремя незнакомцами, и планировщик с queue-воркерами, которые Laravel-приложениям реально нужны. В этом сделка: один клик меняете на контроль.
Деплой Laravel на VPS - больше движущихся частей, чем статический хостинг, но каждая маленькая, и весь путь механический, если вы его уже прошли. Вот он, с двумя ошибками, о которые люди спотыкаются первыми. (Если вы сразу пошли в Laravel Cloud или Forge, это карта для self-hosted, которая понадобится, когда счёт за managed станет громким.)
Часть ссылок на этой странице - партнёрские. На наши рекомендации они не влияют - см. дисклеймер о партнёрских ссылках.
Что вы собираете
Стандартная продакшн-раскладка: PHP-FPM выполняет ваше Laravel-приложение, Nginx завершает TLS и передаёт подходящие запросы в PHP-сокет, а один долгоживущий queue-воркер плюс одна строка в cron делают асинхронную работу. Один сервер, одно приложение, без оркестрации.
Internet → Nginx (:443, TLS) → PHP-FPM (unix socket) → MySQL / PostgreSQL
│
├─ queue worker (systemd)
└─ scheduler (одна запись cron)
| Слой | Что делает | Ключевая настройка на этой странице |
|---|---|---|
| PHP-FPM | Выполняет приложение, по одному запросу за раз, из пула | listen.owner = www-data |
| Composer | Ставит и подключает зависимости | composer install --no-dev --optimize-autoloader |
| Nginx | Входная дверь: TLS + маршруты в public/index.php |
try_files + fastcgi_pass unix:...php-fpm.sock |
| Certbot | Выпускает и продлевает бесплатные сертификаты Let’s Encrypt | sudo certbot --nginx -d app.example.com |
| База данных | MySQL или PostgreSQL, на ваш выбор | DB_CONNECTION в .env |
| Systemd | Держит queue-воркер живым после перезагрузок | WantedBy=multi-user.target |
| Cron | Запускает планировщик Laravel каждую минуту | * * * * * php artisan schedule:run |
1. Возьмите сервер
Возьмите Ubuntu LTS VPS - 26.04 «Resolute Raccoon» - это текущий LTS (выпущен 23 апреля 2026), 24.04 всё ещё поддерживается.[1] Фиксируйте LTS, а не новейший промежуточный релиз. Если вы всё ещё решаете, нужен ли вам вообще выделенный сервер, сравнение VPS vs shared-хостинг покрывает, когда хватит шаред-плана, а когда он начнёт мешать.
- 4 ГБ RAM - комфортный минимум для Laravel + PHP-FPM + MySQL на одной машине; 2 ГБ работают, если вынести базу или смириться со свопом во время
composer install. - Выбирайте любого провайдера (DigitalOcean, Linode, Vultr, Hetzner). Наше сравнение тарифа $6 покрывает различия, которые реально влияют на выбор.
2. Сначала захлопните дверь
Прежде чем трогать PHP, защитите машину. Это шаг, который пропускают и о котором жалеют в 3 часа ночи.
# от root: создаём пользователя deploy и даём ему sudo
adduser deploy
usermod -aG sudo deploy
# только вход по ключу
mkdir -p /home/deploy/.ssh
cp ~/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh && chmod 600 /home/deploy/.ssh/authorized_keys
# файрвол: только SSH, HTTP, HTTPS
ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw enable
Затем в /etc/ssh/sshd_config поставьте PermitRootLogin no и PasswordAuthentication no, перезапустите SSH (sudo systemctl restart ssh) и не закрывайте текущую сессию, пока не убедитесь, что новый вход работает.
3. Установите PHP и Composer
Laravel 13 требует PHP 8.3 или новее и поддерживает PHP 8.3-8.5.[2] На Ubuntu 26.04 дистрибутив поставляет PHP 8.5; фиксируйте поддерживаемую линию, с которой согласны ваши пакеты. Последние стабильные релизы выходят на php.net, PHP 8.6 на начало ноября 2026 находится в пререлизном тестировании.[3]
sudo apt update
sudo apt install -y nginx php-fpm php-cli php-mysql php-pgsql \
php-mbstring php-xml php-curl php-zip php-bcmath php-gd php-intl
Установка тянет расширения, которые нужны Laravel: ctype, cURL, DOM, fileinfo, filter, hash, mbstring, OpenSSL, PCRE, PDO, session, tokenizer и XML.[2] Если вы используете конкретный путь сокета, запомните точную версию PHP-FPM, которая поставилась - Ubuntu называет её php8.5-fpm, и сокет идёт следом.
Установите Composer глобально:
sudo php -r "copy('https://getcomposer.org/installer', '/tmp/composer-setup.php');"
sudo php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer
Composer 2.11 - текущая версия на 30 октября 2026.[4]
4. Получите код и настройте .env
Склонируйте приложение в собственную директорию и отдайте её deploy:
sudo mkdir -p /srv
sudo chown deploy:deploy /srv
git clone https://github.com/you/myapp.git /srv/myapp
cd /srv/myapp
Скопируйте файл окружения и заполните прод-значения. Стандартный .env из composer create-project рассчитан на локальную разработку, поэтому APP_DEBUG=true и APP_ENV=local нужно перевернуть:
cp .env.example .env
APP_NAME="My App"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://app.example.com
DB_CONNECTION=pgsql # or mysql
DB_HOST=127.0.0.1
DB_PORT=5432 # 3306 для mysql
DB_DATABASE=myapp
DB_USERNAME=myapp
DB_PASSWORD=CHANGE_ME
В проде APP_DEBUG обязан быть false - документация прямо пишет, что оставить его true - значит рисковать утечкой чувствительных значений конфигурации конечным пользователям.[2]
5. Установите зависимости и выполните миграции
composer install --no-dev --optimize-autoloader
php artisan migrate --force
--no-dev пропускает dev-пакеты, --optimize-autoloader уплощает автолоадер для скорости в проде. --force выполняет миграции без подтверждения - важно в CI-сборке или скриптованном деплое.
Задайте права на запись там, где это нужно Laravel:
sudo chown -R www-data:www-data /srv/myapp/storage /srv/myapp/bootstrap/cache
6. Настройте Nginx для PHP-FPM
Создайте /etc/nginx/sites-available/myapp. Это по сути тот сервер-блок, который документирует Laravel, и единственное правило, которое нельзя нарушать: каждый запрос должен доходить до public/index.php - никогда не отдавайте из корня проекта, это выставит .env и другие чувствительные файлы в публичный интернет.[2] Про логику каждой директивы прокси - в нашем полном гайде по обратному прокси Nginx.
server {
listen 80;
listen [::]:80;
server_name app.example.com;
root /srv/myapp/public;
index index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ ^/index\.php(/|$) {
fastcgi_pass unix:/var/run/php/php8.5-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_buffer_size 32k;
fastcgi_buffers 8 32k;
fastcgi_busy_buffers_size 64k;
fastcgi_hide_header X-Powered-By;
}
location ~ \.php$ { return 404; }
location ~ /\.(?!well-known).* { deny all; }
}
Подправьте путь сокета под точную версию PHP-FPM, которую вы поставили (выше php8.5-fpm.sock). Включите сайт и проверьте:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Две детали стоит сохранить: блок location ~ \.php$ { return 404; } заставляет необработанные .php-пути отдавать 404 вместо исполнения, а deny all на dotfile-ы (кроме .well-known для ACME-челленджей) держит конфиги и логи недосягаемыми.[2]
7. Бесплатный SSL через Let’s Encrypt
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d app.example.com
sudo certbot renew --dry-run
Плагин Nginx переписывает ваш сервер-блок, добавляя TLS и редирект с :80 - сам плагин изменяет конфиг сервера, а затем настраивает автопродление.[5]
8. Оптимизация для прода
Laravel даёт одну команду, которая кэширует config, events, routes и views; запускайте её как часть каждого деплоя:
php artisan optimize
Один подвох, о который спотыкается каждый хоть раз: после php artisan config:cache файл .env больше не загружается, и каждый вызов хелпера env() вернёт null.[2] Значения конфига в коде читайте через config(), а если меняете переменные окружения - обязательно php artisan config:clear (или optimize:clear) и повторный кэш. Fallback APP_ENV и прочее живут в скомпилированном конфиге - это задумано, а не баг.
9. Queue-воркер как systemd-сервис
Дефолтное queue-соединение Laravel - база данных, и задания выполняет долгоживущий воркер - php artisan queue:work.[6] Этот процесс должен переживать перезагрузки, что и делает systemd (если не хотите юзерспейс-демон, наше сравнение PM2 vs systemd поможет решить на источниках).
Создайте /etc/systemd/system/queue-worker.service:
[Unit]
Description=Laravel queue worker
After=network.target mysql.service postgresql.service
[Service]
User=deploy
Group=www-data
WorkingDirectory=/srv/myapp
ExecStart=/usr/bin/php artisan queue:work --sleep=3 --tries=3
Restart=always
RestartSec=3
Environment=QUEUE_CONNECTION=database
[Install]
WantedBy=multi-user.target
Затем:
sudo systemctl daemon-reload
sudo systemctl enable --now queue-worker
sudo systemctl status queue-worker
--tries=3 ограничивает повторы, чтобы отравленное задание не крутилось вечно, и переводит его в таблицу failed_jobs, которую смотрите через php artisan queue:failed.[6] После каждого деплоя с новым кодом заданий воркер нужно перезапускать, чтобы он подхватил новый код - php artisan reload завершает перезапускаемые сервисы, либо перезапустите юнит напрямую.
10. Планировщик через одну строку cron
Вместо записи в crontab на каждое задание планировщик Laravel выполняет всё, что объявлено в routes/console.php, из одной записи cron.[7] Добавьте её через crontab -e для пользователя deploy:
* * * * * cd /srv/myapp && php artisan schedule:run >> /dev/null 2>&1
Что и когда запускать решает сам планировщик - ежеминутная cron-запись это документированный паттерн, и Laravel пропускает всё не наступившее.[7] Посмотрите, что зарегистрировано, через php artisan schedule:list.
11. Выберите базу: MySQL или PostgreSQL
Laravel поддерживает обе, так что это про вашу нагрузку. PostgreSQL 18 - текущий стабильный мажор (минор 18.6 на начало ноября 2026), поддержка до 2030 года.[8] Актуальная LTS-линия MySQL - это 9.7 (9.7.4, вышла 20 октября 2026), а инновационная линия - 26.10.[9] Достаточно php-mysql или php-pgsql плюс подходящий коннектор в .env; реальные инженерные различия мы разбираем в PostgreSQL managed vs self-hosted. Для одного Laravel-приложения обычного пакета mysql-server или postgresql на машине достаточно - managed не покупайте, пока его не оправдывают только аптайм или бэкапы.
12. Альтернатива на Docker Compose
Если ручное управление PHP-FPM и systemd-юнитами ощущается дорого, Docker Compose сжимает весь стек в один декларативный файл - приложение, база и воркер как сервисы. Это тот маршрут, который мы сравниваем как следует в Docker vs raw VPS; короткая версия - Compose меняет гарантии systemd «установил один раз, переживает перезагрузку» на воспроизводимость ценой ещё одной движущейся части на сервере.
Минимум: сервис приложения из PHP-FPM базового образа с вашим кодом, сервис базы данных, сервис nginx, который монтирует public/ приложения и проксирует в PHP-FPM, и сервис-воркер на php artisan queue:work. Nginx по-прежнему завершает TLS - либо монтируйте сертификаты Certbot, либо пусть Traefik/Caddy на хосте делают ACME-танец. Queue-воркер и планировщик - просто дополнительные сервисы в том же файле, что и главный выигрыш: один docker compose up -d поднимает всё приложение, а проблема надзора за процессами, которую systemd решает руками, решается рестартами контейнеров.
Траблшутинг двух реальных отказов
502 Bad Gateway от Nginx. PHP-FPM недосягаем. Проверьте, что сокет существует и пул жив: sudo systemctl status php8.5-fpm, затем сверьте путь сокета в конфиге Nginx с реальным /run/php/*.sock. Несовпадение версий (php8.3-fpm.sock в конфиге при установленном php8.5) - обычная причина.
Белый экран после деплоя. В девяти случаях из десяти это APP_DEBUG=false, прячущее фатал, плюс устаревший config:cache. Откройте /srv/myapp/storage/logs/laravel.log, затем перезапустите php artisan optimize после любого изменения .env. Поведение config:cache из доки - .env игнорируется, env() возвращает null - корень проблемы, когда в логах пропавшие значения конфига вместо реального исключения.[2]
Кратко «вот и всё»
VPS даёт вам точную версию PHP, приватную базу и асинхронную машинерию, под которую и задуман Laravel: один php artisan queue:work под systemd, одна строка cron для планировщика, впереди Nginx + PHP-FPM. Версионно-чувствительные части - мажор PHP (Laravel 13 хочет 8.3+) и путь сокета в конфиге Nginx. Зафиксируйте эти две - и всё остальное (.env, certbot, optimize) механическое.
Источники
- Ubuntu 26.04 LTS «Resolute Raccoon» - релизное объявление Canonical (23.04.2026). https://ubuntu.com/blog/canonical-releases-ubuntu-26-04-lts-resolute-raccoon - checked 2026-11-08
- Laravel 13.x - Deployment (требования к серверу, конфиг Nginx, config:cache, APP_DEBUG). https://laravel.com/docs/13.x/deployment - checked 2026-11-08
- PHP - Downloads & supported versions (стабильный 8.5, в тестировании 8.6 RC). https://www.php.net/downloads - checked 2026-11-08
- Composer - Download (v2.11.1, 30.10.2026). https://getcomposer.org/download/ - checked 2026-11-08
- Certbot - Using
--nginx(плагин изменяет конфиг сервера). https://eff-certbot.readthedocs.io/en/stable/using.html - checked 2026-11-08 - Laravel 13.x - Queues (queue:work, –tries, failed_jobs, reload). https://laravel.com/docs/13.x/queues - checked 2026-11-08
- Laravel 13.x - Task Scheduling (одна запись cron, schedule:run). https://laravel.com/docs/13.x/scheduling - checked 2026-11-08
- PostgreSQL - Versioning policy (18.6 текущий, поддержка до 2030). https://www.postgresql.org/support/versioning/ - checked 2026-11-08
- MySQL - 9.7 Release Notes (9.7.4, 20.10.2026). https://dev.mysql.com/doc/relnotes/mysql/9.7/en/ - checked 2026-11-08