Every web application eventually needs to do something that's too slow to do while a user waits. Sending an email, resizing an image, generating a report, calling an outside API. If you do these things during the request, pages feel sluggish and users get impatient. If the outside service is slow or fails, your whole page can fail with it.

Queues solve this by moving slow work into the background. The user clicks a button, the app says "done," and the actual work happens a moment later. Laravel makes this very easy, and it supports several queue drivers. The two most common starting points are the database driver and Redis, and a lot of developers wonder which one to use.

This post compares them in a practical way, so you can choose with confidence and know when it might be time to change.

A quick look at how queues work in Laravel

When you dispatch a job, Laravel stores a description of that job in the queue. One or more worker processes keep checking the queue, pick up jobs, and run them. If a job fails, Laravel can retry it a few times, and if it keeps failing, it moves it to a failed jobs table so you can look at it later.

The queue driver decides where those jobs are stored while they wait. That's the main difference between the options.

The database driver

With the database driver, jobs are stored in a table in your existing database.

The biggest advantage is simplicity. You don't need any extra software. You create the table, run a worker, and you're done. It's easy to understand, easy to debug, because you can look at the jobs with an ordinary query, and the jobs are stored durably along with the rest of your data.

The downside is that workers have to keep checking the database, which adds load. For a small application, that's no problem. But if you process many jobs, the queue can start competing with your regular application queries, and throughput is generally lower than it would be with an in-memory option.

The Redis driver

Redis is an in-memory data store, which means it's very fast at the kind of quick reads and writes a queue needs.

The advantages are speed and tooling. Redis can handle a much higher volume of jobs, and it takes load off your main database. It also works with Laravel Horizon, a dashboard that shows you what's in your queues, how fast jobs are processed, which ones have failed, and how long they took. For anyone running a lot of background work, that visibility is extremely helpful.

The drawbacks are mostly operational. You have to run and maintain a Redis service, which is one more thing to monitor, secure, and keep updated. Durability also depends on how you configure it. Because Redis keeps data in memory, you need to set up persistence thoughtfully if losing a job would be a real problem.

Other options worth knowing about

Laravel supports other drivers too, including Amazon SQS and Beanstalkd. Managed queue services like SQS can remove much of the operational work, since someone else keeps the system running. They're a good choice for teams that want reliability without managing their own infrastructure, though they come with their own pricing and setup.

How to decide

Rather than looking for the "best" driver, think about your situation.

How many jobs do you process? A few hundred a day is a very different situation from thousands per minute. At low volume, nearly any driver works.

How bad is it if a job is lost or delayed? If a lost job means a missed invoice, you'll want to think carefully about durability. If it means a delayed thumbnail, you can relax a bit.

How will you notice problems? Good monitoring is easier with Redis and Horizon, but you can also build a simple dashboard for the database driver.

What can your team operate? A smaller setup may be the wiser choice for a small team. An extra service you don't have time to maintain isn't an upgrade.

What does it cost? Compare the cost of running an additional service against the cost of giving your database more capacity.

Keep your workers running

Whichever driver you pick, queue workers are long-running processes, and they need to be looked after. A process manager like Supervisor is commonly used to restart workers if they crash or stop. Without it, a worker can quietly die, and jobs pile up unnoticed.

Workers also hold your application code in memory. That means after you deploy new code, you need to restart them so they pick up the changes. Forgetting this is a classic source of confusing bugs, where the web part of your app behaves one way and the background jobs behave another.

Make your jobs safe to retry

Queues work best when jobs can safely run more than once. Retries happen, and sometimes a job gets processed twice because of a timeout or a worker restart. If running a job twice would charge a customer twice or send two emails, that's a design problem you want to catch early.

A common approach is to check whether the work has already been done before doing it, and to use unique identifiers where it matters. It's a small habit that prevents some very embarrassing mistakes.

Start simple, move when needed

A reasonable path for many projects is to start with the database driver. It's simple, it works, and it lets you focus on building the product. When you notice real signs of strain, such as slow job processing, extra load on the database, or a need for better monitoring, you can switch to Redis. Because Laravel hides most of the differences behind the same job code, the move is usually easier than people expect.

Monitoring and failed jobs

Whichever driver you choose, don't ignore failed jobs. They're an early warning system. A growing pile of failures usually means something has changed, like an outside service going down, a bad deployment, or a bug that only appears with certain data.

Set up a simple routine: check the failed jobs table or dashboard regularly, get alerts when failures spike, and decide in advance which failures should be retried and which need a human to look at them. Also keep an eye on how long jobs wait before they start. If that delay keeps growing, you need more workers or a faster setup, and you'll know before users start asking why their emails are late.

A few handy settings to know about

Laravel lets you control how many times a job should be retried, how long it can run before it's considered stuck, and how long to wait between attempts. Taking a few minutes to set these deliberately, instead of relying on defaults, can prevent a lot of strange behavior. For example, a job that calls a slow outside service might need a longer timeout, while a job that sends a notification might be fine with a short one.

You can also use separate queues for different kinds of work, so urgent jobs, like password reset emails, don't get stuck behind a big batch of slower ones, like report generation. It's a small design choice that makes the system feel much more responsive.

Wrapping up

The database driver is a great starting point for small applications and low volumes, thanks to its simplicity. Redis is a common next step when speed, volume, or monitoring needs grow, especially with Horizon. There's no need to over-plan. Begin with what's simplest, keep your workers supervised, design jobs to be safe to retry, and upgrade when your own numbers tell you it's time.