Laravel Queues Guide
Asynchronous job processing in Laravel: job classes, dispatching patterns, failed job handling, batching, and queue worker configuration.
1. Creating a Job
// php artisan make:job ProcessArticleImages
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 ProcessArticleImages implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 3; // max attempts
public int $timeout = 120; // seconds before timeout
public int $backoff = 60; // seconds between retries
public bool $deleteWhenMissingModels = true; // auto-delete if model deleted
public function __construct(
private readonly Article $article,
private readonly array $options = []
) {}
public function handle(ImageProcessor $processor): void
{
foreach ($this->article->getRawImages() as $image) {
$processor->resize($image, $this->options['maxWidth'] ?? 1200);
$processor->generateWebp($image);
}
$this->article->update(['images_processed' => true]);
}
public function failed(\Throwable $e): void
{
// Called when all retries are exhausted
\Log::error("Image processing failed for article {$this->article->id}", [
'error' => $e->getMessage(),
]);
$this->article->update(['images_processed' => false]);
}
}
2. Dispatching Jobs
use App\Jobs\ProcessArticleImages;
// Dispatch immediately (to configured queue)
ProcessArticleImages::dispatch($article);
// Dispatch with delay
ProcessArticleImages::dispatch($article)->delay(now()->addMinutes(10));
// Dispatch on specific queue
ProcessArticleImages::dispatch($article)->onQueue('media');
// Dispatch on specific connection
ProcessArticleImages::dispatch($article)->onConnection('redis');
// Dispatch if condition
ProcessArticleImages::dispatchIf($article->has_images, $article);
ProcessArticleImages::dispatchUnless($article->images_processed, $article);
// After database commits (avoids race conditions)
ProcessArticleImages::dispatch($article)->afterCommit();
// Sync dispatch (runs immediately, no queue)
ProcessArticleImages::dispatchSync($article);
// Chaining jobs
ProcessArticleImages::dispatch($article)
->chain([
new GenerateThumbnails($article),
new NotifySubscribers($article),
]);
3. Job Batching
use Illuminate\Bus\Batch;
use Illuminate\Support\Facades\Bus;
$jobs = $articles->map(fn($a) => new ProcessArticleImages($a))->all();
$batch = Bus::batch($jobs)
->name('Process Article Images')
->then(function (Batch $batch) {
// All jobs succeeded
\Log::info("Batch {$batch->id} completed successfully");
})
->catch(function (Batch $batch, \Throwable $e) {
// First job failure
\Log::error("Batch failed: " . $e->getMessage());
})
->finally(function (Batch $batch) {
// Always runs (success or failure)
})
->allowFailures() // don't cancel batch on single failure
->onQueue('media')
->dispatch();
// Check batch progress
$batch = Bus::findBatch($batch->id);
echo $batch->progress(); // 0-100
4. Failed Job Handling
// config/queue.php
'failed' => [
'driver' => 'database-uuids',
'database' => env('DB_CONNECTION', 'mysql'),
'table' => 'failed_jobs',
],
// Artisan commands
// php artisan queue:failed โ list failed jobs
// php artisan queue:retry all โ retry all failed jobs
// php artisan queue:retry {id} โ retry specific job
// php artisan queue:forget {id} โ delete specific job
// php artisan queue:flush โ delete all failed jobs
// Custom failed job handler
class SendWelcomeEmail implements ShouldQueue
{
use InteractsWithQueue;
public function failed(\Throwable $e): void
{
// Can re-dispatch with different config
$this->release(60); // re-queue with 60s delay
// Or notify team
Notification::send(
User::admin()->get(),
new JobFailedNotification($this, $e)
);
}
}
5. Queue Worker Commands
# Start worker processing 'default' queue
php artisan queue:work
# Process specific queue with options
php artisan queue:work redis --queue=media,default --sleep=3 --tries=3 --timeout=90
# Process single job then exit
php artisan queue:work --once
# Listen (restarts on code changes โ dev only)
php artisan queue:listen
# Supervisor config (production)
# /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/html/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=4
redirect_stderr=true
stdout_logfile=/var/www/html/storage/logs/worker.log
6. Queue Drivers Comparison
| Driver | Best For | Pros |
|---|---|---|
| sync | Testing / simple | No setup needed |
| database | Small apps | Built-in, no extra infra |
| redis | Production | Fast, supports Horizon |
| beanstalkd | Medium apps | Simple, reliable |
| sqs | AWS environments | Fully managed |