⏱️
Хостинг

Laravel queue и scheduler на сервере: cron, Supervisor и обычный хостинг

10.11.2026
← Все статьи

В Laravel тяжёлые задачи вроде отправки писем и SMS, генерации отчётов или обработки изображений обычно ставятся в очередь (queue), а повторяющиеся действия поручаются планировщику (scheduler). На локальной машине разработчик запускает php artisan queue:work в терминале, и всё работает. После выкладки на сервер задачи начинают копиться в очереди и не выполняются, потому что процесс, который их читает, никто не запустил. Разберём, как сделать так, чтобы обработчик очереди и планировщик работали на сервере постоянно и надёжно.

Как на самом деле работают queue и scheduler

Система очередей состоит из двух частей. Приложение записывает задачу в очередь, например в таблицу jobs базы данных или в Redis. Отдельный постоянно работающий процесс queue:work следит за очередью, забирает задачи и выполняет их. Если процесс не запущен, задачи не теряются, но и не выполняются никогда. Планировщик устроен иначе: Laravel сам не следит за временем, его нужно каждую минуту «будить» снаружи командой php artisan schedule:run. Эта команда проверяет, у каких задач подошло время, и выполняет только их.

Практический пример: частная клиника в Ташкенте принимает записи пациентов на сайте misol.uz. При записи в очередь ставится SMS с подтверждением, а каждое утро в 9:00 рассылаются напоминания о завтрашнем приёме. Для первой задачи нужен постоянно работающий обработчик очереди, для второй — планировщик, который будят каждую минуту. Если не настроено что-то одно, пациенты не получают сообщений, и клиника узнаёт об этом только из жалоб.

Выбор и подготовка драйвера очереди

Для обычных проектов удобнее всего драйвер базы данных: он не требует дополнительных сервисов. Укажите в .env значение QUEUE_CONNECTION=database. В Laravel 11 таблицы jobs и failed_jobs входят в стандартные миграции, в более старых версиях их создают отдельной командой. При большой нагрузке или тысячах задач Redis работает быстрее, но его придётся установить самостоятельно на VPS.

# Laravel 10 и более ранние версии
php artisan queue:table
php artisan queue:failed-table
php artisan migrate --force

Одна строка cron для планировщика

В Laravel 11 запланированные задачи описываются в routes/console.php, в старых версиях — в методе schedule файла app/Console/Kernel.php. В примере с клиникой напоминания задаются так:

use Illuminate\Support\Facades\Schedule;

Schedule::command('reminders:send')
    ->dailyAt('09:00')
    ->timezone('Asia/Tashkent');

Обратите внимание на часовой пояс: сервер может работать по UTC, и без timezone напоминания уйдут на пять часов позже. Чтобы будить задачи, на сервере нужна всего одна строка cron, всё остальное расписание Laravel ведёт сам:

* * * * * cd /var/www/misol && php artisan schedule:run >> /dev/null 2>&1

На обычном хостинге эту строку добавляют в разделе планировщика (cron) панели ISPmanager. Важный момент: на хостинге может быть установлено несколько версий PHP, и команда php может вызвать не ту, что вы ожидаете. В таком случае указывайте полный путь, например /opt/php82/bin/php; точный путь можно уточнить в техподдержке sayt.uz.

Постоянный обработчик с Supervisor на VPS

queue:work — долгоживущий процесс, который может остановиться из-за утечки памяти, перезагрузки сервера или неожиданной ошибки. Supervisor следит именно за такими процессами и сразу перезапускает их при остановке. В Ubuntu или Debian он ставится так:

sudo apt install supervisor

Затем создайте файл /etc/supervisor/conf.d/misol-worker.conf:

[program:misol-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/misol/artisan queue:work database --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/misol/storage/logs/worker.log
stopwaitsecs=3600

Здесь numprocs=2 запускает два параллельных обработчика, а --max-time=3600 заставляет каждый из них завершиться через час, после чего Supervisor поднимает его заново. Так память не будет постепенно заполняться. Значение stopwaitsecs должно быть больше длительности самой долгой задачи, иначе при остановке она оборвётся на середине. Конфигурация загружается командами:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start "misol-worker:*"
sudo supervisorctl status

Что делать на обычном хостинге без Supervisor

На shared-хостинге Supervisor обычно не установить, а постоянные фоновые процессы ограничены. В этом случае очередь можно обрабатывать через сам планировщик: каждую минуту запускается обработчик, выполняет все задачи в очереди и сам завершается, когда она опустеет.

Schedule::command('queue:work --stop-when-empty --max-time=50')
    ->everyMinute()
    ->withoutOverlapping();

withoutOverlapping не запустит новый обработчик, пока не завершился предыдущий, поэтому процессы не будут накапливаться. Задачи при этом могут задерживаться до минуты, но для писем с подтверждением и SMS это обычно незаметно. Если в проекте есть задачи на несколько минут, например конвертация видео или большой экспорт, правильнее перейти на VPS.

Деплой, ошибки и контроль

Обработчик очереди загружает код приложения в память один раз и не видит новых изменений. Поэтому после каждого деплоя выполняйте команду ниже: обработчики закончат текущую задачу, мягко завершатся, и Supervisor запустит их уже с новым кодом.

php artisan queue:restart

Задачи, которые не удалось выполнить и после трёх попыток, попадают в таблицу failed_jobs. Их можно посмотреть командой php artisan queue:failed, а после устранения причины повторно отправить через php artisan queue:retry all. Периодически просматривайте и логи в storage/logs: недоступный SMS-шлюз или сменившийся пароль почты чаще всего проявляются именно там. Тогда пациенты клиники будут получать сообщения вовремя, а вы увидите проблему раньше клиентов.

Похожие статьи

🚦 Ошибка «Too many connections» в MySQL: причины и решение 📁 Подключение к хостингу по SFTP через FileZilla и WinSCP 🗄️ Как создать базу данных и пользователя в ISPmanager 🔤 utf8mb4: как сохранить эмодзи и узбекские буквы в MySQL без искажений
🌐 Язык
🇺🇿 O'zbek 🇺🇿 Ўзбек 🇷🇺 Русский ✓ 🇬🇧 English