Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Queue Laravel Package

illuminate/queue

The Laravel Queue component offers a unified API across multiple queue backends, letting you defer time-consuming tasks like sending email until later so web requests stay fast. Includes a Capsule for easy standalone configuration outside Laravel.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and look at the source, not the README. The README is a stub; the real reference is laravel.com/docs/queues plus the package's own src/. The files you will actually read are Capsule/Manager.php, QueueManager.php, Queue.php, Worker.php, WorkerOptions.php, Jobs/*.php, and Console/*.php.

composer require illuminate/queue:^11.0

Be aware the dependency graph is not small — illuminate/queue pulls container, contracts, support, collections, console, database, filesystem, pipeline, encryption and several Symfony components. illuminate/database is required because model serialization (SerializesModels) lives there. For chains/batches you also need illuminate/bus; for ShouldBeUnique / WithoutOverlapping you need illuminate/cache; for event hooks you need illuminate/events.

2. Bootstrap the Capsule once, in a single file that both web and worker processes include.

use Illuminate\Queue\Capsule\Manager as Queue;

$queue = new Queue;                       // creates its own Illuminate\Container
$queue->addConnection([
    'driver'    => 'redis',
    'connection'=> 'default',
    'queue'     => 'default',
    'retry_after' => 90,
], 'default');                            // name 'default' becomes the default connection

$queue->setAsGlobal();                    // enables Queue::push(...) facade-style calls

3. First use case: move one slow thing off the request. Take your slowest controller action (sending mail, hitting a third-party API, generating a PDF), extract it into a job class with a handle() method, and call $queue->push(SendWelcomeEmail::class, ['userId' => $user->id]). Then run a worker (see below) and confirm the request latency drop. That single loop — push, worker, verify — is 80% of what this package does for you.

4. Run a worker. If you are inside Laravel, php artisan queue:work redis is all you need and this package is the implementation behind it. Standalone, the cleanest path is a ~30-line CLI script using Illuminate\Queue\Worker (see Implementation Patterns). The Console/* commands in the package (queue:work, queue:retry, queue:failed, queue:monitor, queue:clear) are real, but wiring an Illuminate\Console\Application plus an event dispatcher outside Laravel is more work than the worker loop.


Implementation Patterns

The dispatch boundary. Push only after the DB transaction commits. In Laravel that's dispatchAfterResponse / DB::afterCommit; standalone, call $queue->push(...) after commit() returns, never inside the transaction — otherwise a worker can pick up a job whose row does not exist yet.

Job class design. Keep constructors scalar-only (int $userId, not a User), and resolve dependencies in handle():

class SendWelcomeEmail implements ShouldQueue
{
    public int $tries = 3;
    public int $backoff = [10, 60, 300];   // exponential-ish
    public int $timeout = 30;
    public string $queue = 'mail';

    public function __construct(public int $userId) {}

    public function handle(Mailer $mailer): void { /* ... */ }
}

ShouldQueue is in illuminate/contracts; InteractsWithQueue, SerializesModels, Queueable traits are in this package.

Worker loop (standalone).

use Illuminate\Queue\Worker;
use Illuminate\Queue\WorkerOptions;

$worker = new Worker(
    $queueManager,          // Illuminate\Queue\QueueManager (expose it from your Capsule subclass)
    $events,                // Illuminate\Contracts\Events\Dispatcher
    $exceptions,            // Illuminate\Contracts\Debug\ExceptionHandler
    fn () => false          // isDownForMaintenance
);

$options = new WorkerOptions(maxTries: 3, timeout: 30, sleep: 1, memory: 256);
while (true) {
    $status = $worker->runNextJob('default', 'mail', $options); // 'job' | 'failed' | 'idle'
}

runNextJob() — not daemon() — is the loop primitive you want in a custom runner; it returns after one job, so you control signal handling, logging and shutdown yourself.

Multiple connections / priorities. Register several connections and route jobs to them:

$queue->addConnection($redisConfig, 'critical');
$queue->pushOn('critical', ChargeCard::class, $payload);
$queue->connection('critical')->later(300, Recheck::class, $data);

Run separate worker pools per connection — a slow mail queue must never starve critical.

Batches and chains. These are not in this package's responsibility surface; install illuminate/bus and use Bus::chain([...])->dispatch() / Bus::batch([...])->then(...)->dispatch(). pushRaw() exists as an escape hatch but you lose retry metadata, so use it only for cross-language producers.

Event hooks. Bind an events dispatcher in the container and listen for Illuminate\Queue\Events\JobProcessing, JobProcessed, JobFailed, JobExceptionOccurred, WorkerStopping. This is the correct integration point for metrics, Sentry, structured logging and "job started/finished" audit rows — it works regardless of whether you use the Capsule or a hand-built Worker.

Testing. Illuminate\Support\Testing\Fakes\QueueFake ships in illuminate/support, so Queue::fake() works standalone: assert assertPushedOn('mail', SendWelcomeEmail::class, fn ($job) => $job->userId === 5). For the job body itself, instantiate and call handle() directly with a fake dependency — far faster and more reliable than driving a real worker in CI.


Gotchas and Tips

  • This is a read-only subtree split. Never open a PR here; all changes go to laravel/framework, and tags track framework releases (v11.x.y). Pin a caret constraint and treat upgrades as framework upgrades. With zero standalone dependents, nobody is testing the non-Laravel path for you.
  • retry_after must be greater than the worker timeout. Get this backwards and a job silently runs twice — once by the timed-out worker, once by the re-delivered copy. This is the single most common production bug with this package.
  • $timeout is a no-op without pcntl. The worker enforces timeouts via pcntl_alarm. Check extension_loaded('pcntl') in the worker container or your "timeouts" are decorative.
  • The container split between web and worker is a real failure mode. $queue->push(FooJob::class) stores only the class string; the worker's container must be able to make() that class. If your worker bootstrap differs from your web bootstrap (different config, missing bindings, missing service providers), jobs fail at run time, not dispatch time. Share one bootstrap file.
  • ShouldBeUnique, WithoutOverlapping, RateLimited, ThrottlesExceptions all require a cache repository. Without illuminate/cache bound, these middlewares either throw or silently never lock. ShouldBeUnique additionally needs the Illuminate\Contracts\Bus\Dispatcher (i.e. illuminate/bus) for release-on-completion.
  • failed_jobs is your responsibility standalone. Configure a FailedJobProvider (Illuminate\Queue\Failed\DatabaseFailedJobProvider or DatabaseUuidFailedJobProvider) and create the table yourself — the columns are uuid, connection, queue, payload, exception, failed_at. Without it, queue:failed, queue:retry, queue:forget and --tries exhaustion all have nowhere to land and failures disappear.
  • Payload size limits. SQS caps at 256 KB and Beanstalkd at 64 KB. Never serialize a file or a big model graph — pass an ID or a storage path and re-fetch in handle().
  • Never put closures or resources in job data. CallQueuedClosure works only because of laravel/serializable-closure; your own payloads should be scalars, arrays of scalars, or SerializesModels Eloquent models.
  • At-least-once, not exactly-once. Design every handler to be idempotent (unique key on the write, or an INSERT ... ON CONFLICT DO NOTHING). $this->attempts() tells you the retry count; $this->release($seconds) re-queues deliberately; $this->delete() short-circuits.
  • Driver quirks worth knowing before choosing: the database driver polls (fine to ~a few hundred jobs/min, and needs MySQL 8+/Postgres 9.5+ for SKIP LOCKED to avoid lock contention); redis wants the block_for option set to avoid busy-polling; SQS caps delay at 15 minutes and needs a visibility timeout ≥ your job timeout; Beanstalkd uses ttr, which is its own retry_after.
  • addConnection() with name 'default' is how you set the default connection — not a separate setter. Anything else requires setDefaultConnection().
  • Log the JobProcessing/JobFailed events with the job's uuid and correlate to your request IDs; queue debugging without a correlation ID is guesswork.
  • queue:restart needs a shared cache. If you build your own loop, implement graceful shutdown yourself: catch SIGTERM, let the in-flight job finish, then exit. Worker::stop() and the WorkerStopping event are the supported hooks.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
thomas-0816/doctrine-dbal-duckdb
moyisoft/laravel-admin
oxygen/mod-pages
oxygen/mod-import-export
oxygen/mod-media
ksoft/laravel-contentbuilderjs
ourgoldpackages/exchange
mikkimike/laravel-exchange1c
wandesnet/mercadopago-laravel
tsfcorp/email
larafony/core
laravel-enso/currencies
tartan/laravel-online-payment
gam/laravel-sat-catalogs
cloudcreativity/laravel-stripe
abdulelahragih/querybuilder
wedevbr/celcoin-laravel
surazdott/nepali-date
devsquad-cockpit/laravel
coding-socks/laravel-upload-handler