Driver Comparison at a Glance
Laravel supports multiple queue backends. The right choice depends on your infrastructure,
traffic, and budget. Here's the honest breakdown:
-
sync — runs jobs immediately in the same process. No queue, no worker.
Use only in local development or testing.
-
database — stores jobs in a MySQL/PostgreSQL table. Zero extra infrastructure.
Works on any shared hosting. Best for small to medium apps (<100 jobs/minute).
-
redis — stores jobs in Redis (in-memory). Much faster than database.
Supports Laravel Horizon for real-time monitoring. Best for production apps with high job volume.
-
Amazon SQS — fully managed, serverless queue. No worker management.
Scales to millions of jobs. Best for AWS-hosted applications or when you don't want to manage infrastructure.
-
beanstalkd — lightweight dedicated queue server. Less popular today.
Redis is a better choice in most cases.
Database Driver – Setup & Deep Dive
The database driver is the easiest to get started with because you already have a database.
Jobs are stored in a jobs table and workers poll it on a set interval.
Step 1: Configure .env
QUEUE_CONNECTION=database
Step 2: Create the Jobs Table
php artisan queue:table
php artisan migrate
This creates the jobs table with these columns:
id - bigint (primary key)
queue - varchar (queue name, e.g. "default", "emails")
payload - longtext (serialized job class + data)
attempts - tinyint (how many times it's been tried)
reserved_at - timestamp (when a worker picked it up)
available_at - timestamp (when it becomes available, for delayed jobs)
created_at - timestamp
Step 3: Create the Failed Jobs Table
php artisan queue:failed-table
php artisan migrate
Step 4: Run the Worker
php artisan queue:work database --queue=emails,default --tries=3
How the Database Driver Works Internally
The worker uses a SELECT + UPDATE pattern to "lock" a job atomically so two workers don't pick up the same job.
It sets reserved_at on the row, processes it, then deletes it.
This works fine for low to medium job volumes but creates database load at high throughput
because every job = multiple database writes.
The Problem with Database Queue at Scale
At high job throughput (1000+ jobs/minute), the constant polling and row locking creates
significant database load. The jobs table becomes a bottleneck.
This is the point where you should migrate to Redis.
⚠️ Database queue polling interval is controlled by --sleep=3 (seconds). Lower values mean faster job pickup but more DB queries when the queue is empty.
Redis Driver – Setup & Deep Dive
Redis stores your jobs in memory, making it orders of magnitude faster than the database driver.
It uses blocking list operations so workers are notified of new jobs instantly — no polling needed.
Step 1: Install Predis
composer require predis/predis
Step 2: Configure .env
QUEUE_CONNECTION=redis
REDIS_CLIENT=predis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
Step 3: Configure database.php
// config/database.php — redis section
'redis' => [
'client' => env('REDIS_CLIENT', 'predis'),
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_DB', 0),
],
'cache' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_CACHE_DB', 1),
],
],
⚠️ Use a separate Redis database index for cache vs queue. Set REDIS_DB=0 for queues and REDIS_CACHE_DB=1 for cache. This prevents cache flushes from wiping your pending jobs.
Step 4: Run the Worker
php artisan queue:work redis --queue=emails,default --tries=3
phpredis vs predis
Laravel supports two Redis PHP clients:
predis — pure PHP library. No PHP extension needed. Easy to install with Composer. Slightly slower but good enough for most apps.
phpredis — C extension. Faster than predis. Requires installing the PHP extension on the server. Use this in high-throughput production environments.
Laravel Horizon — Redis Queue Monitoring
When using Redis, you can install Laravel Horizon for a beautiful dashboard to monitor
your queues in real time — job throughput, wait times, failed jobs, and more:
composer require laravel/horizon
php artisan horizon:install
php artisan horizon
Horizon replaces queue:work as your queue runner and adds metrics, auto-scaling worker counts, and a web dashboard at /horizon.
💡 Related: Laravel Horizon deserves its own full blog post — it transforms how you manage Redis queues in production.
Amazon SQS – Setup & Deep Dive
Amazon SQS is a fully managed message queue service. You don't manage any servers — AWS handles
availability, scaling, and delivery. If your app runs on AWS, SQS is often the best choice.
Step 1: Install the AWS SDK
composer require aws/aws-sdk-php ~3.0
Step 2: Create an SQS Queue in AWS Console
Go to AWS Console → SQS → Create Queue
Choose Standard (not FIFO) for most Laravel use cases
Note the Queue URL (e.g. https://sqs.us-east-1.amazonaws.com/123456789/my-queue)
Create an IAM user with sqs:* permissions and note the Access Key and Secret
Step 3: Configure .env
QUEUE_CONNECTION=sqs
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_DEFAULT_REGION=us-east-1
SQS_QUEUE=https://sqs.us-east-1.amazonaws.com/123456789/my-queue
SQS_PREFIX=https://sqs.us-east-1.amazonaws.com/123456789
Step 4: Configure config/queue.php
'sqs' => [
'driver' => 'sqs',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
'queue' => env('SQS_QUEUE', 'default'),
'suffix' => env('SQS_SUFFIX'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'after_commit' => false,
],
SQS Message Visibility Timeout
SQS has an important concept called visibility timeout.
When a worker picks up a job, SQS hides it from other workers for the visibility timeout duration.
If the job doesn't finish in time, it becomes visible again and another worker picks it up.
Set your SQS visibility timeout to match (or slightly exceed) your job's $timeout value.
Mismatch here causes duplicate job processing — a common SQS gotcha.
⚠️ SQS has a maximum message size of 256KB. If your job payload is larger (e.g. base64 encoded files), you'll get errors. Always pass references (IDs, paths) in your job, not the actual data.
SQS Standard vs FIFO
Standard Queue — at-least-once delivery, best-effort ordering. Highest throughput. Use for most Laravel jobs.
FIFO Queue — exactly-once delivery, strict ordering. Lower throughput (3000 messages/second max). Use when job order and deduplication are critical (e.g. financial transactions).
Using Multiple Connections Together
You're not limited to one queue connection. You can use different connections for different jobs
in the same application — for example, Redis for high-priority emails and SQS for bulk processing:
// Dispatch to Redis queue
SendWelcomeEmail::dispatch($user)
->onConnection('redis')
->onQueue('emails');
// Dispatch to SQS for bulk work
ProcessBulkExport::dispatch($export)
->onConnection('sqs')
->onQueue('exports');
You can also set connection and queue as properties on the job class:
class ProcessBulkExport implements ShouldQueue
{
public string $connection = 'sqs';
public string $queue = 'exports';
// ...
}
Conclusion
Choosing the right queue driver is an infrastructure decision that affects performance, cost, and reliability.
Here's a simple decision guide:
Use database — you're on shared hosting, just starting out, or have low job volume
Use redis — you have a VPS/dedicated server, need speed, or want Horizon monitoring
Use SQS — your app is on AWS, you need infinite scale, or you want zero queue-server management
Use multiple connections — route different job types to the most appropriate backend