Laravel Queues in Production: What Nobody Tells You
LaravelQueuesBackendProduction

Laravel Queues in Production: What Nobody Tells You

Beyond the docs — real patterns for reliable queue jobs, retry strategies, and monitoring that I use in a production SaaS application.

Mokammel Tanvir

Mokammel Tanvir

Software Engineer

The 2am Failure Nobody Noticed

The Laravel docs cover queues well enough to get started. They don't cover what happens six months later when a job silently fails at 2am and nobody finds out until Monday morning.

I've been running queues in production at Smart Provider LLC for over a year. Here's what I actually do now.

The Job Structure I Always Start With

Every job I write includes these properties by default. No exceptions.

class ProcessAIWorkflow implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $tries   = 3;
    public int $backoff = 60;   // seconds between retries
    public int $timeout = 120;  // never leave this as unlimited

    public function __construct(private readonly int $recordId) {}

    public function handle(ClaudeService $claude): void
    {
        $record = Record::findOrFail($this->recordId);

        if ($record->isAlreadyProcessed()) {
            return; // idempotency check — retries happen
        }

        $record->update([
            'processed_at' => now(),
            'result'       => $claude->extract($record->raw_data),
        ]);
    }

    public function failed(Throwable $e): void
    {
        Log::error('ProcessAIWorkflow failed', [
            'record_id' => $this->recordId,
            'error'     => $e->getMessage(),
        ]);

        Record::find($this->recordId)?->update(['status' => 'failed']);
    }
}

The $timeout Default Will Burn You

Laravel's default job timeout is unlimited. That means a job that hangs — network issue, slow API, infinite loop — keeps the worker process blocked indefinitely. Other jobs pile up behind it.

Set $timeout on every job. For AI calls I use 120 seconds. For emails, 30. For webhooks, 15. The number depends on the job, but unlimited is never correct.

Make Jobs Idempotent

Retries happen. A worker can die mid-execution and the job gets requeued. If your job isn't idempotent — running it twice produces the same result — you'll eventually create duplicate data or process something twice.

The isAlreadyProcessed() check above is the simplest form. For critical operations, use a cache lock:

public function handle(): void
{
    $lock = Cache::lock("process_record_{$this->recordId}", 120);

    if (!$lock->get()) {
        return; // another worker has this one
    }

    try {
        // do the work
    } finally {
        $lock->release();
    }
}

Horizon Config That Separates Priorities

Running everything through a single default queue works for side projects. For production SaaS, you need queue priorities — AI jobs are slow and shouldn't block email delivery.

'production' => [
    'supervisor-ai' => [
        'connection' => 'redis',
        'queue'      => ['ai-heavy'],
        'processes'  => 5,
        'tries'      => 3,
        'timeout'    => 180,
    ],
    'supervisor-default' => [
        'connection' => 'redis',
        'queue'      => ['default', 'emails', 'webhooks'],
        'processes'  => 10,
        'tries'      => 3,
        'timeout'    => 60,
    ],
],

Separate supervisors per queue. Slow AI jobs can't starve fast transactional ones.

Monitoring

Laravel Telescope is great for local and staging — query timeline, job inspection, all of it. In production it's too heavy. Horizon gives you queue monitoring with minimal overhead: throughput, failure rates, processing times.

Set up a Horizon notification for when queue depth exceeds a threshold. If jobs are piling up, I want to know before a user files a support ticket.

The difference between a reliable queue system and an unreliable one usually comes down to three things: $timeout, idempotency, and actually checking Horizon's failed jobs list.

All Posts
Mokammel Tanvir

Mokammel Tanvir

Full-Stack Engineer · Laravel · Vue · WordPress · AI

Building web applications with Laravel, Vue/Nuxt, and WordPress — SaaS platforms, REST APIs, and AI-integrated workflows. Open to remote and hybrid opportunities.