Benjamin Crozat “Heard about Sevalla? They let you deploy PHP apps with ease.” Claim $50 →

The Laravel failover queue driver: stop losing jobs

4 minutes read

The Laravel failover queue driver: stop losing jobs

What is the Laravel failover queue driver?

It’s a queue driver named failover and available starting in Laravel 12.34. First, list your queue connections in order in config/queues.php. Then, when you push a job, Laravel tries the first one. If it fails, it automatically tries the next one and so on.

Benefits:

  • It’s effortless.
  • You only have your config to change.
  • You have less chances of losing jobs to dead connections.

Quick start (copy-paste)

1. Set your default queue to failover

In .env:

QUEUE_CONNECTION=failover

2. Add a failover connection

In config/queue.php:

'connections' => [
    // Normal connections…
    'database' => [
        'driver' => 'database',
        'table' => 'jobs',
        'queue' => 'default',
        'retry_after' => 90,
        'after_commit' => false,
    ],

    'redis' => [
        'driver' => 'redis',
        'connection' => 'default',
        'queue' => 'default',
        'retry_after' => 90,
        'block_for' => null,
        'after_commit' => false,
    ],

    // NEW: the Laravel failover queue driver.
    'failover' => [
        'driver' => 'failover',
        'connections' => [
            // Try Redis first. If it fails, fall back to database.
            'redis',
            'database',
        ],
    ],
],

Now dispatch jobs like you always do:

SendWelcomeEmail::dispatch($user->id);

If Redis is down, the job is pushed to the database queue instead.

How to run workers and Horizon (don’t point them at failover)

The failover driver falls back when pushing jobs. When consuming jobs, the failover connection only reads from the first listed connection (for example, redis). So if a push fell back to database, a worker pointed at failover won’t see those jobs.

Run workers for each connection you might use:

# Keep your fast path.
php artisan queue:work redis --queue=default

# Also run a worker for the fallback path.
php artisan queue:work database --queue=default

Horizon example (config/horizon.php)

'supervisors' => [
    'redis' => [
        'connection' => 'redis',
        'queue' => ['default'],
        'balance' => 'simple',
        'maxProcesses' => 5,
    ],
    'database' => [
        'connection' => 'database',
        'queue' => ['default'],
        'balance' => 'simple',
        'maxProcesses' => 2,
    ],
],

This way, if pushes fall back to database, you still have a worker ready to process those jobs.

Three practical patterns (pick one)

A. Speed first, safety second (Redis → database)

Good for most apps.

'connections' => [
    'failover' => [
        'driver' => 'failover',
        'connections' => ['redis', 'database'],
    ],
],

B. Small tools and admin panels (Redis → sync)

If anything breaks, just run the job now so users aren’t blocked.

'connections' => [
    'failover' => [
        'driver' => 'failover',
        'connections' => ['redis', 'sync'],
    ],
],

C. Bigger apps (Redis → SQS → database)

Keep performance and add cloud durability.

'connections' => [
    'failover' => [
        'driver' => 'failover',
        'connections' => ['redis', 'sqs', 'database'],
    ],
],

How you can test the failover driver

  1. Start your worker(s) (Horizon or php artisan queue:work for each connection).
  2. Stop Redis to simulate a failure:
    • Docker: docker stop redis
    • Homebrew: brew services stop redis
  3. Dispatch a job.
  4. Check your logs and, if you use database fallback, check the jobs table:
SELECT id, queue, attempts, available_at
FROM jobs
ORDER BY id DESC
LIMIT 10;
  1. Start Redis again when you’re done.

You’ll see jobs pushed to the next connection when Redis is down.

Get a heads-up when failover happens (simple event listener)

Laravel fires a QueueFailedOver event when it switches connections. You can log it (or ping Slack).

App\Providers\EventServiceProvider.php

protected $listen = [
    \Illuminate\Queue\Events\QueueFailedOver::class => [
        \App\Listeners\NotifyOnQueueFailover::class,
    ],
];

App\Listeners\NotifyOnQueueFailover.php

namespace App\Listeners;

use Illuminate\Support\Facades\Log;
use Illuminate\Queue\Events\QueueFailedOver;

class NotifyOnQueueFailover
{
    public function handle(QueueFailedOver $event): void
    {
        Log::critical('Queue failover occurred', [
            'failed_connection' => $event->connectionName,
            'job' => is_object($event->command) ? get_class($event->command) : $event->command,
        ]);

        // Or send to Slack / email from here.
    }
}

Now you’ll know right away when the Laravel failover queue driver had to save you.

Gotchas I noted while testing

  • Failover is for pushing. It doesn’t merge or drain multiple connections later. Workers won’t “fall through” to read another connection.
  • If all listed connections fail at push-time, Laravel throws an exception and the job is not queued.
  • pushRaw() won’t fire the QueueFailedOver event; only push() and later() do.
  • Make jobs idempotent (safe to run twice with the same result) in case a flaky connection causes retries.
  • If you fall back to database or sqs, make sure your jobs serialize cleanly.

FAQ

Does this work with Horizon?

Yes. But don’t point Horizon at failover. Run a supervisor for each connection you list (for example, one for redis, one for database).

Can I use any connection type?

Yes. redis, sqs, database, beanstalkd, or even sync.

Final thought

I like features that are “set it and forget it.” The Laravel failover queue driver is exactly that. You set QUEUE_CONNECTION=failover, list your backups in config/queue.php, and your app keeps moving when one service goes down. That’s less stress for you, and a smoother experience for your users.


Did you like this article? Then, keep learning:

Help me reach more people by sharing this article on social media!

0 comments

Guest

Markdown is supported.

Hey, you need to sign in with your GitHub account to comment. Get started →

Great tools for developers

Search for posts and links

Try to type something…