Очередь на базе в Laravel: настройка и частые ошибки
Пошаговая настройка драйвера database для очередей в Laravel 11: создание таблиц, запуск воркера и разбор ошибок, из-за которых задачи молча теряются или дублируются.

За 20–30 минут вы поднимете очередь задач в Laravel 11 на драйвере database: создадите таблицы, отправите первую задачу в фон, запустите воркер и научитесь понимать, почему задача повисла или выполнилась дважды. Драйвер database не требует Redis или отдельного брокера — очередь живёт в той же базе, что и приложение. Для интернет-магазина на паре тысяч задач в сутки этого достаточно.
Что нужно до начала
- Laravel 11.x (в 10.x путь к конфигу отличается — об этом ниже);
- настроенное подключение к базе (MySQL, PostgreSQL или SQLite) в
.env, миграции приложения уже проходят; - доступ к консоли на сервере, где вы сможете держать запущенный процесс воркера (SSH, а не только веб-панель);
- права на запуск composer и artisan-команд.
Если php artisan migrate у вас падает с ошибкой подключения — сначала чините базу, очередь тут ни при чём.
Шаг 1. Переключить драйвер очереди
По умолчанию свежий Laravel 11 ставит QUEUE_CONNECTION=database в .env. Проверьте эту строку. Если стоит sync — задачи выполняются прямо в запросе, никакой фоновой обработки нет, и это первая ловушка: код «работает», но ничего не откладывается.
# .env
QUEUE_CONNECTION=database
После правки .env сбросьте кеш конфига, иначе значение не подхватится:
php artisan config:clear
Проверка: команда php artisan queue:table на следующем шаге не должна ругаться на неизвестное подключение.
Шаг 2. Создать таблицы очереди
Драйвер database хранит задачи в таблице jobs, а упавшие — в failed_jobs. В Laravel 11 генератор миграции такой:
php artisan make:queue-table
php artisan migrate
В более старом Laravel 10 команда называется php artisan queue:table, а для упавших задач — php artisan queue:failed-table. В 11.x таблица failed_jobs обычно уже входит в стартовые миграции — проверьте каталог database/migrations, прежде чем генерировать её повторно, иначе получите дубль миграции.
Проверка: после migrate в базе появятся таблицы jobs и failed_jobs. Посмотрите список: php artisan db:table jobs или напрямую в клиенте базы.
Шаг 3. Создать и отправить задачу
Сгенерируйте класс задачи:
php artisan make:job SendOrderConfirmation
Файл появится в app/Jobs/SendOrderConfirmation.php. Вот минимальный рабочий вариант:
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class SendOrderConfirmation implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
// сколько раз пробовать перед пометкой failed
public int $tries = 3;
// хранить только id, а не всю модель
public function __construct(public int $orderId) {}
public function handle(): void
{
// здесь тяжёлая работа: отправка письма, вызов внешнего API
// берём свежие данные из базы по id
}
}
Отправка в очередь из контроллера:
use App\Jobs\SendOrderConfirmation;
SendOrderConfirmation::dispatch($order->id);
Проверка: после вызова dispatch в таблице jobs появится новая строка. Если строки нет — вернитесь к шагу 1, скорее всего драйвер остался sync.
Шаг 4. Запустить воркер
Задача лежит в таблице, но сама себя не выполнит. Её забирает воркер:
php artisan queue:work
Команда запускает процесс, который читает jobs, выполняет handle() и удаляет успешно завершённые строки. Пока процесс работает — задачи обрабатываются. Закрыли терминал — обработка встала.
Проверка: в консоли появятся строки вида Processing: App\Jobs\SendOrderConfirmation, а затем Processed. Строка в таблице jobs исчезнет.
Главное правило:queue:workдержит код задачи в памяти на всё время жизни процесса. Изменили класс задачи и не перезапустили воркер — он крутит старую версию. Командаqueue:restartпосылает сигнал на мягкую остановку, чтобы супервизор поднял процесс заново.
Частые ошибки новичков
Здесь ошибки, которые не выдают явную трассировку, а просто дают «ничего не происходит» или «сработало дважды».
Оставили QUEUE_CONNECTION=sync и удивляетесь, что фона нет
При sync задача выполняется в том же запросе синхронно. Строк в jobs не появляется, воркер простаивает. Симптом: пользователь ждёт ответа дольше, чем должен, а таблица очереди пустая.
Забыли перезапустить воркер после деплоя
После обновления кода запущенный queue:work продолжает выполнять задачи по старому классу. Правьте деплой так, чтобы он всегда вызывал php artisan queue:restart в конце.
Гоняете queue:work без супервизора
Процесс упал по фатальной ошибке или сервер перезагрузился — воркер не поднимется сам. Очередь копится в jobs, задачи не выполняются, и об этом никто не узнаёт, пока клиент не пожалуется. Для постоянной работы нужен процесс-менеджер (Supervisor на Linux или Laravel Horizon для драйвера Redis).
Кладёте в задачу всю Eloquent-модель
Трейт SerializesModels сериализует только ключ модели, а при выполнении подтягивает её заново. Если между отправкой и выполнением запись удалили, задача упадёт с ModelNotFoundException. Передавайте id и внутри handle() проверяйте, что запись ещё существует.
Не настроили tries и timeout — задача крутится вечно
Без $tries задача при ошибке будет возвращаться в очередь снова и снова. Задайте лимит попыток и таймаут выполнения. Значение таймаута воркера должно быть больше самого долгого handle(), иначе процесс убьёт задачу на середине.
Полезные флаги queue:work
| Флаг | Что делает | Когда нужен |
|---|---|---|
--tries=3 | Лимит попыток на уровне воркера | Если не задан $tries в классе |
--timeout=60 | Максимум секунд на одну задачу | Для задач с внешними вызовами |
--sleep=3 | Пауза, когда очередь пуста | Снизить нагрузку на базу |
--queue=high,default | Порядок обработки очередей | Когда есть приоритетные задачи |
--max-jobs=1000 | Перезапуск процесса после N задач | Борьба с утечками памяти |
Точный список и поведение флагов сверяйте в документации своей версии — часть опций появлялась постепенно.
Если не получилось
- Задачи не появляются в таблице
jobs. Драйвер осталсяsyncили конфиг закеширован. Выполнитеphp artisan config:clearи проверьтеQUEUE_CONNECTION. Base table or view not found: jobs. Не прогнали миграцию таблиц очереди. Вернитесь к шагу 2.- Задача выполнилась дважды. Вероятно, запущено несколько воркеров, а
handle()не идемпотентен. Либо оставьте один процесс, либо сделайте обработку безопасной к повтору (проверка статуса заказа перед действием). - Строка висит в
jobsи не пропадает. Воркер не запущен или упал. Проверьте, что процесс жив, посмотрите логи вstorage/logs/laravel.log. - Задача попала в
failed_jobs. Посмотрите причину:php artisan queue:failed. Повторить упавшие можно черезphp artisan queue:retry all.
Что дальше
Рабочая связка на одном сервере готова. Дальше стоит настроить Supervisor, чтобы воркер поднимался автоматически и переживал перезагрузку, а в деплой добавить queue:restart. Когда задач станет много и база начнёт заметно нагружаться на таблице jobs, разумно перейти на драйвер Redis — но это отдельная тема, здесь мы разбирали только database. Следите за размером таблицы failed_jobs: если она растёт, значит задачи стабильно падают, и это сигнал, а не мусор для очистки.
Задачи: математика, физика, химия: Разбор задачи по шагам с проверкой и похожей задачей
Спросить за 10 ₽Источники: Laravel Queues — официальная документация
Частые вопросы
У меня Laravel 10, а не 11. Инструкция подойдёт?
Задача выполнилась, но письмо не пришло — где искать?
Можно ли запускать несколько воркеров одновременно?
Что будет, если воркер упадёт посреди выполнения задачи?
Нужно ли чистить таблицу jobs вручную?
Почему при smoke-тесте локально всё работает без воркера?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.