Queue connection config — queues table, horizon, workers
Concept
Failed jobs occur when a job exhausts all retry attempts. Laravel stores failed jobs in the failed_jobs table for inspection and manual re-queuing.
Setup: php artisan queue:failed-table && php artisan migrate creates failed_jobs. Or configure a DynamoDB / Null driver for failed jobs.
What's stored: The serialized job payload, the exception message and stack trace, the connection and queue name, and the failure timestamp.
queue:failed command: php artisan queue:failed — list all failed jobs. php artisan queue:retry {id} — retry a specific failed job. php artisan queue:retry all — retry all failed jobs. php artisan queue:forget {id} — delete a failed job. php artisan queue:flush — delete all failed jobs.
Customizing failed job storage: In config/queue.php, 'failed' key. Default: database-uuids driver using the failed_jobs table. Can use dynamodb, null (disable), or sqs (via custom driver).
Alerting on failures: The job.failed event is fired when a job fails. Listen to it in EventServiceProvider: \Illuminate\Queue\Events\JobFailed::class.
Queue::failing() / Queue::looping(): Callbacks on queue worker lifecycle events.
Horizon failed jobs: Laravel Horizon (lv-19-007) provides a web UI for browsing and retrying failed jobs.
Code Example
<?php
// Create failed_jobs table
// php artisan queue:failed-table
// php artisan migrate
// config/queue.php — failed job configuration
'failed' => [
'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
'database' => env('DB_CONNECTION', 'mysql'),
'table' => 'failed_jobs',
],
// failed_jobs table structure:
// - id (UUID)
// - uuid
// - connection (string) — which queue driver
// - queue (string) — which queue
// - payload (longText) — serialized job
// - exception (longText) — exception with stack trace
// - failed_at (timestamp)
// Artisan commands for managing failed jobs
// php artisan queue:failed — list all
// php artisan queue:retry abc-uuid-123 — retry by ID
// php artisan queue:retry all — retry all
// php artisan queue:forget abc-uuid-123 — delete one
// php artisan queue:flush — delete all
// php artisan queue:prune-failed --hours=48 — delete older than 48h
// Listening for job failures — in EventServiceProvider
protected $listen = [
\Illuminate\Queue\Events\JobFailed::class => [
\App\Listeners\NotifyTeamOfFailedJob::class,
],
];
class NotifyTeamOfFailedJob
{
public function handle(\Illuminate\Queue\Events\JobFailed $event): void
{
\Illuminate\Support\Facades\Log::critical('Queue job failed', [
'connection' => $event->connectionName,
'queue' => $event->job->getQueue(),
'payload' => $event->job->getRawBody(),
'exception' => $event->exception->getMessage(),
]);
// Could also send Slack notification, page on-call, etc.
}
}
// Individual job failure handler
class ProcessOrder implements \Illuminate\Contracts\Queue\ShouldQueue
{
use \Illuminate\Queue\InteractsWithQueue, \Illuminate\Foundation\Bus\Dispatchable;
use \Illuminate\Bus\Queueable, \Illuminate\Queue\SerializesModels;
public function failed(\Throwable $exception): void
{
// Called AFTER all retries exhausted and job is sent to failed_jobs
\Illuminate\Support\Facades\Log::error('Order processing failed permanently', [
'order_id' => $this->orderId,
'exception' => $exception->getMessage(),
]);
// Notify customer, restore inventory, etc.
}
}