Make Laravel After-Commit Queue Dispatch Durable

Laravel’s afterCommit() prevents a worker from seeing uncommitted data, but it creates a second reliability boundary: the database can commit before the queue push runs. For business-critical jobs, store a durable outbox record in the same transaction.

Last updated: November 4, 2026.

<?php
DB::transaction(function () use ($orderData): void {
    $order = Order::create($orderData);

    OutboxMessage::create([
        'event_id' => (string) Str::uuid(),
        'event_type' => 'order.created',
        'payload' => json_encode(['order_id' => $order->id], JSON_THROW_ON_ERROR),
        'published_at' => null,
    ]);
});

// A separate command reads unpublished rows, dispatches them,
// and marks each row only after the queue accepts the job.

The order and outbox row now succeed or roll back together. The relay can retry a queue outage without recreating the business transaction.

Understand what after-commit dispatch guarantees

Laravel’s queue documentation says an after-commit job is held until open parent transactions commit and is discarded when the transaction rolls back. The guide’s queue failover section describes a connection that tries the next configured queue when a push fails.

A current framework report shows why the combination deserves an integration test: the actual push may occur later in the commit callback, outside the code path that originally selected failover. Test the exact Laravel patch version with the primary queue unavailable, dispatch inside a real transaction, and verify that the fallback queue receives the job and a worker processes it.

Use an outbox for required delivery

The transactional outbox pattern records the message beside the business change and lets a separate process publish it. This removes the unsafe dual write between the database and queue, at the cost of eventual rather than immediate dispatch.

Give every outbox event a unique ID. Claim rows in small batches, retry with backoff, and make consumers idempotent because a crash after queue acceptance but before marking the row can produce a duplicate. Monitor the age and count of unpublished rows.

Keep Laravel failover as an availability layer, but do not treat configuration alone as proof of delivery. Continue with running Laravel queue workers, idempotent event processing, and safe SaaS background jobs.

Related Web Cheat Sheet guides

Sergey Kornilov

Sergey Kornilov