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

К концу этой инструкции у вас будет рабочая очередь на драйвере database: HTTP-запрос отдаёт ответ мгновенно, а тяжёлая задача (отправка письма, генерация PDF, вызов внешнего API) выполняется отдельным процессом. Займёт это примерно 20–30 минут. Драйвер database хранит задачи в обычной таблице MySQL или PostgreSQL — ничего дополнительно ставить не нужно, в отличие от Redis или SQS. Для небольших проектов этого хватает; про ограничения скажу в конце.
Инструкция проверена на Laravel 11.x (те же шаги работают и на 10.x). Синтаксис команд Artisan между этими версиями не менялся.
Что нужно до начала
- Установленный проект на Laravel 10.x или 11.x и настроенное подключение к базе (переменные
DB_*в файле.envзаполнены, миграции применяются). - PHP 8.2 или новее — минимальное требование Laravel 11.
- Доступ к терминалу на сервере или локально, где можно держать запущенный процесс
php artisan queue:work. - База данных, поддерживающая транзакции: MySQL/MariaDB или PostgreSQL. SQLite тоже работает, но для продакшена не годится из-за блокировок.
Шаг 1. Указать драйвер очереди
Откройте .env и задайте драйвер:
QUEUE_CONNECTION=databaseПо умолчанию в свежем проекте стоит sync — это значит, что задачи выполняются сразу, в том же процессе, без всякой очереди. Именно поэтому новички не понимают, «почему ничего не откладывается»: пока стоит sync, воркер не нужен и не работает.
Проверка: выполните php artisan config:clear, затем php artisan tinker и внутри наберите config('queue.default'). Должна вернуться строка "database".
Шаг 2. Создать таблицы для очереди
В Laravel 11 команда, которая готовит миграцию таблицы задач:
php artisan make:queue-table
php artisan migrateЕсли вы на Laravel 10.x, команда называется иначе — php artisan queue:table, дальше так же php artisan migrate. Обе создают миграцию для таблицы jobs.
Отдельно стоит создать таблицу для проваленных задач — она пригодится на шаге 6:
php artisan make:queue-failed-table
php artisan migrateПроверка: загляните в базу. Должны появиться таблицы jobs и failed_jobs. В jobs есть колонки queue, payload, attempts, reserved_at, available_at.
Шаг 3. Создать задачу
Сгенерируйте класс задачи:
php artisan make:job SendWelcomeEmailФайл появится в app/Jobs/SendWelcomeEmail.php. Приведите его к такому виду:
<?php
namespace App\Jobs;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeMail;
class SendWelcomeEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
// Сколько раз пробовать задачу, прежде чем признать проваленной
public int $tries = 3;
// Тайм-аут одной попытки в секундах
public int $timeout = 60;
public function __construct(public User $user)
{
}
public function handle(): void
{
Mail::to($this->user->email)->send(new WelcomeMail($this->user));
}
}Ключевой момент — интерфейс ShouldQueue. Без него задача выполнится синхронно, даже если драйвер стоит database. Свойство SerializesModels позволяет передавать в конструктор модель User: в очередь запишется её идентификатор, а при выполнении Laravel заново достанет свежую запись из базы.
Не передавайте в конструктор задачи большие объекты или коллекции целиком. В очередь пишется сериализованный payload, и «толстая» задача раздувает таблицуjobsи замедляет её чтение. Передавайте идентификаторы, а данные подтягивайте внутриhandle().
Шаг 4. Отправить задачу в очередь
В контроллере или сервисе поставьте задачу в очередь методом dispatch:
use App\Jobs\SendWelcomeEmail;
// после регистрации пользователя
SendWelcomeEmail::dispatch($user);Этот вызов не отправляет письмо. Он пишет строку в таблицу jobs и мгновенно возвращает управление. Пользователь получает ответ, письмо уйдёт позже — когда до задачи доберётся воркер.
Проверка: вызовите этот код (например, зарегистрируйте тестового пользователя), затем посмотрите в таблицу jobs. Там появится новая строка. Письмо ещё не ушло — так и должно быть.
Шаг 5. Запустить воркер
Строки в таблице jobs сами по себе не выполнятся. Их разбирает отдельный процесс:
php artisan queue:workПосле запуска в терминале появятся строки вида Processing и Processed с именем класса задачи. Строка из таблицы jobs исчезнет, письмо уйдёт.
queue:work против queue:listen
Есть две похожие команды. Разница принципиальная:
| Команда | Что делает | Когда использовать |
|---|---|---|
queue:work | Держит приложение в памяти, задачи выполняются быстро | Продакшен под супервизором |
queue:listen | Перезагружает код на каждой задаче, медленнее | Локальная разработка, когда код часто меняется |
Важный нюанс queue:work: он держит код в памяти и не видит ваших правок в задачах, пока его не перезапустить. После деплоя или изменения класса задачи выполняйте php artisan queue:restart — это сигнал воркерам мягко завершиться после текущей задачи.
Шаг 6. Обработать провалы
Если внутри handle() вылетело исключение, задача повторяется до значения $tries. Исчерпав попытки, она попадает в таблицу failed_jobs. Посмотреть провалы:
php artisan queue:failedПовторить конкретную проваленную задачу по её идентификатору:
php artisan queue:retry {id}Повторить все сразу — php artisan queue:retry all. Очистить список провалов — php artisan queue:flush.
Шаг 7. Держать воркер живым в продакшене
Команда queue:work, запущенная руками, умрёт вместе с закрытым терминалом. На сервере процессом должен управлять супервизор — обычно это Supervisor. Минимальный конфиг для файла в /etc/supervisor/conf.d/:
[program:laravel-queue]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/app/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
user=www-data
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/app/storage/logs/queue-worker.logФлаг --max-time=3600 заставляет воркер завершиться через час, чтобы Supervisor поднял свежий процесс — так вы избегаете утечек памяти при длительной работе. Точный список опций queue:work для вашей версии сверяйте в документации Laravel, раздел Queues.
Если не получилось
- Задача выполняется сразу, воркер не нужен. В
.envосталсяQUEUE_CONNECTION=syncлибо не сброшен кеш конфига. Выполнитеphp artisan config:clear. - Class "App\Jobs\SendWelcomeEmail" not found. Забыли импорт
use App\Jobs\SendWelcomeEmail;в контроллере или опечатка в namespace. - Строка в jobs появляется, но не исчезает. Воркер не запущен либо запущен на другом подключении/очереди. Проверьте, что
queue:workреально работает и смотрит на ту же базу. - Base table or view not found: jobs. Не применили миграцию из шага 2. Выполните
php artisan migrate. - Правки в задаче не применяются. Работает старый
queue:workс кодом в памяти. Выполнитеphp artisan queue:restart.
Что дальше
Очередь работает — теперь стоит настроить следующее. Отслеживание: подключите --tries и уведомление в метод failed() внутри задачи, чтобы узнавать о провалах. Мониторинг глубины очереди: считайте строки в jobs, растущее число значит, что воркер не справляется — добавьте процессов через numprocs в Supervisor. Когда драйвер database упрётся в блокировки таблицы под нагрузкой, переходите на Redis: смена сводится к драйверу в .env, код задач менять не придётся. Отложенный запуск (dispatch()->delay(...)) и приоритеты очередей — отдельная тема, здесь мы её не разбирали.
Задачи: математика, физика, химия: Разбор задачи по шагам с проверкой и похожей задачей
Спросить за 10 ₽Источники: Laravel 11.x — Queues, Laravel — Deploying with Supervisor
Частые вопросы
У меня Laravel 10, а команда make:queue-table не находится. Что делать?
Можно ли использовать драйвер database в продакшене?
Задача не выполняется, хотя воркер запущен. Где искать причину?
Почему мои изменения в классе задачи не применяются?
Что произойдёт, если задача упадёт с ошибкой?
Нужен ли Redis, если я уже настроил очередь на базе?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.