Open-source project
vyuldashev/laravel-queue-rabbitmq avatar
vyuldashev/laravel-queue-rabbitmq

Inside vyuldashev/laravel-queue-rabbitmq: queue options, exchanges and Horizon

RabbitMQ driver for Laravel Queue. Supports Laravel Horizon.

2,131 stars458 forksPHPMIT

At a glance

What is it?
A Laravel queue driver that keeps job classes untouched while exposing broker features the database backend cannot hold, including per-queue priority, exchange routing keys, failed message rerouting and Horizon worker events. The value is real, and the support table above those sections is not.
Who is it for?
Adopt this driver if your Laravel app already talks to a RabbitMQ broker, or if you need exchange routing, per-queue priority and failed message rerouting that a database queue cannot express. Leave it alone if your queue handles a handful of jobs a day, or if you would rather not own exchange bindings and queue arguments yourself.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 51 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Laravel jobs reach RabbitMQ without rewriting the job classes

Laravel's queue dispatches a job class to a driver, and the driver decides what physically carries it. Change the driver and the job classes, the dispatch call and the worker command stay exactly where they are. That is the whole arrangement here: a connection carrying `'driver' => 'rabbitmq'` sits in the same `connections` array as every other backend, and the classes behind it live under the `VladimirYuldashev\LaravelQueueRabbitMQ` namespace.

What the swap buys is broker behaviour a database table cannot express. Laravel keeps failed jobs in the database by default, a flat record with no exchange, no routing key and no priority. This driver adds config keys for all three, plus a worker key that tells Laravel which queue API to use.

It suits teams that already run a broker, or that want off the database queue untouched. It is not a consumer library and not a job framework. Everything specific it offers sits under the `options` key of one connection, and none of it is needed to push a first message through.

The composer package name is not the repository name

The repository is `vyuldashev/laravel-queue-rabbitmq`. Composer does not install that name. The package on Packagist is `vladimir-yuldashev/laravel-queue-rabbitmq`, and the badges at the top of the page point at the same longer string, so copy it exactly:

bash
composer require vladimir-yuldashev/laravel-queue-rabbitmq

The package registers itself after install, so there is no provider to wire up by hand. The only edit that has to happen is the connection block in `config/queue.php`.

To have something to point at, the repository ships a `docker-compose.yml` for its own test suite. It runs the broker with TLS files mounted from the test fixtures, and certificate checking is deliberately disabled there:

yaml
  rabbitmq:
    image: rabbitmq:3.8
    environment:
      RABBITMQ_SSL_CACERTFILE: /rootCA.pem
      RABBITMQ_SSL_VERIFY: verify_none
      RABBITMQ_SSL_FAIL_IF_NO_PEER_CERT: "false"
    volumes:
      - "./tests/files/rootCA.pem:/rootCA.pem:ro"
      - "./tests/files/rootCA.key:/rootCA.key:ro"
    ports:
      - "5671:5671"
      - "5672:5672"

Treat that as a test harness, not a production template. The image is `rabbitmq:3.8`, and a second service brings up the management image on the same four ports: `15671` and `15672` for the interface, `5671` for TLS AMQP, `5672` for plain AMQP.

Connection config is an array of hosts, each with a vhost

Every RabbitMQ connection is a list of host entries, not a single host key. Each entry carries its own `host`, `port`, `user`, `password` and `vhost`, so one connection can name several brokers:

php
'connections' => [
    // ...

    'rabbitmq' => [

       'driver' => 'rabbitmq',
       'hosts' => [
           [
               'host' => env('RABBITMQ_HOST', '127.0.0.1'),
               'port' => env('RABBITMQ_PORT', 5672),
               'user' => env('RABBITMQ_USER', 'guest'),
               'password' => env('RABBITMQ_PASSWORD', 'guest'),
               'vhost' => env('RABBITMQ_VHOST', '/'),
           ],
           // ...
       ],

       // ...
    ],

    // ...
],

The defaults matter more than they look. Untouched, this points at `127.0.0.1` on port `5672`, as the user `guest`, with the password `guest`, on the root vhost `/`. Those are development broker defaults. An app that copies the block without setting the five environment variables ships the broker's own credentials.

The documented entry also carries no TLS field, and no other key for it appears anywhere on the page. A broker that insists on TLS on `5671`, the same port the test compose file exposes, cannot be described by this block alone.

Queue options set priority once for every queue on that connection

Queue behaviour is configured once per connection, under `options` then `queue`, and every queue created for that connection inherits the properties. The first knob covers delayed messages:

php
'options' => [
    'queue' => [
        // ...

        'prioritize_delayed' =>  false,
        'queue_max_priority' => 10,
    ],
],

Two details hide in that snippet. `prioritize_delayed` is false in the example, so the behaviour stays off until you flip it. And leave the max priority out and the package sets it to 2 when the feature is used. Two levels, not ten, unless `queue_max_priority` names the ceiling you want.

The page says nothing about what happens to a queue that already exists in the broker under a different maximum, or whether existing queues are re-declared when the value changes. If a deployment alters `queue_max_priority` after the queue was first created, check the queue's arguments in the management interface before assuming the new ceiling took effect.

Exchanges with routing keys move binding ownership to your side

The second group of queue options changes where a published message lands. Without them the driver publishes straight to the queue it is addressing. With them it publishes to an exchange, carrying a routing key:

php
'options' => [
    'queue' => [
        // ...

        'exchange' => 'application-x',
        'exchange_type' => 'topic',
        'exchange_routing_key' => '',
    ],
],

Three defaults sit behind it. An omitted exchange means RabbitMQ uses `amq.direct` for the routing key. An omitted routing key makes the routing key the `queue` name. A `%s` inside the routing key is replaced with the queue name, which is how one exchange fans out across many queues.

The catch is ownership. When you route through an exchange with a routing key, you create the queues and their bindings yourself; the driver does not declare them, and no confirm or retry step is documented for a message that matches nothing. Anyone using exchanges here keeps a second piece of state, the bindings, outside Laravel.

Failed jobs exist twice once reroute_failed is switched on

Laravel writes every failure to the database. This driver can also hand the failed message to the broker, which matters when a process other than Laravel has to react:

php
'options' => [
    'queue' => [
        // ...

        'reroute_failed' => true,
        'failed_exchange' => 'failed-exchange',
        'failed_routing_key' => 'application-x.%s',
    ],
],

The naming follows the same rules as the publish options. An omitted failed exchange means `amq.direct`. An omitted failed routing key takes the queue name with `.failed` substituted in, and `%s` is replaced by the queue name.

The consequence is that a failure lives in two places at once: the Laravel failed job table and the broker. Nothing in the documented options says which write survives when the broker is unreachable during the reroute, and no retention limit is set on the failed queue. That copy is a side channel you drain yourself, and you decide what happens to it.

Foreign payloads need a job class of your own

Some traffic on a RabbitMQ queue is not produced by Laravel at all. Those messages do not follow Laravel's job payload schema, so a worker cannot work out which class to execute and the message sits there.

The escape hatch is to extend the built-in job class and point the connection at your subclass:

php
'options' => [
    'queue' => [
        // ...

        'job' => \App\Queue\Jobs\RabbitMQJob::class,
    ],
],
php
<?php

namespace App\Queue\Jobs;

use VladimirYuldashev\LaravelQueueRabbitMQ\Queue\Jobs\RabbitMQJob as BaseJob;

class RabbitMQJob extends BaseJob
{

    /**
     * Fire the job.
     *
     * @return void
     */
    public function fire()

The key applies to every message that connection retrieves, not to one queue. A mixed queue carrying both Laravel jobs and foreign messages cannot be expressed, because you choose a single wrapper class per connection and that class decides for all of them how a payload maps to a class.

Horizon reports on the worker, and the worker key is off by default

Horizon arrived at version 8.0 of this package. It needs two things: Horizon installed first, and the connection told which queue API to use, because Horizon depends on the events the worker dispatches:

php
'connections' => [
    // ...

    'rabbitmq' => [
        // ...

        /* Set to "horizon" if you wish to use Laravel Horizon. */
       'worker' => env('RABBITMQ_WORKER', 'default'),
    ],

    // ...
],

Set the `RABBITMQ_WORKER` environment variable to `horizon` and that value reaches Laravel as the queue API Horizon expects. Leave it alone and Horizon still installs cleanly, the driver still runs, jobs still get processed, and the metrics have nothing behind them.

Now distrust the support table at the top of the page. It carries one row, package version 13 paired with Laravel 9 and bug fixes until August 8th, 2023, while v14.2.0 is labelled the first version compatible with Laravel 12 and v14.4.0 shipped on 2025-11-13. That row is a lagging record of a line two majors behind, not a current support matrix. The last push was on 2026-09-02 and the repository is not archived.

Editorial conclusion

Adopt this driver if your Laravel app already talks to a RabbitMQ broker, or if you need exchange routing, per-queue priority and failed message rerouting that a database queue cannot express. Leave it alone if your queue handles a handful of jobs a day, or if you would rather not own exchange bindings and queue arguments yourself. Before deploying, check the version pair you installed against your Laravel version instead of the support table at the top of the README, and set RABBITMQ_WORKER if Horizon is installed.

Frequently asked questions

Does vyuldashev/laravel-queue-rabbitmq work with Laravel Horizon?

Horizon support has been in place since 8.0, in two steps: install Horizon first, then set the RABBITMQ_WORKER environment variable to horizon so the connection uses the queue API Horizon expects. The worker key defaults to default, and Horizon depends on events dispatched by the worker.

How do I install vyuldashev/laravel-queue-rabbitmq?

With composer require vladimir-yuldashev/laravel-queue-rabbitmq, which is a different name from the repository itself. The package registers itself, after which you add a rabbitmq connection carrying a driver key and a hosts array to config/queue.php.

How do I give delayed jobs a higher priority in vyuldashev/laravel-queue-rabbitmq?

Set prioritize_delayed to true under the queue options of the connection and set queue_max_priority to the ceiling you want, for example 10. When the max priority is left out, the package sets it to 2 when the feature is used.

Can vyuldashev/laravel-queue-rabbitmq handle messages published by another application?

Yes, through the job option in the connection's queue options. Point it at a class extending the built-in RabbitMQJob, and every message that connection retrieves gets wrapped by your class instead of the built-in one.

Where do failed jobs go with vyuldashev/laravel-queue-rabbitmq?

Laravel stores failed jobs in the database as usual. With reroute_failed set to true the failed message is also routed to a failed exchange, and the failed routing key gets the queue name substituted for %s, with .failed appended when the routing key is omitted.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. vyuldashev/laravel-queue-rabbitmq on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/vyuldashev-laravel-queue-rabbitmq.svg)](https://hysenlabs.com/projects/vyuldashev-laravel-queue-rabbitmq)

Was this useful?