Using Queues and Jobs to postpone Tasks in Laravel
Content Index
- Solution: delegating operations through queues
- Jobs and queues
- Benefits
- Queue driver
- Configuration
- The jobs and the queues
- The jobs
- The queues
- Create your own queues in Laravel to maintain order
- Connections for the structure to maintain the queues
- Configure the Queue Driver or connector for the database
- Create the jobs table in our database
- Creating our first Job in Laravel
- Dispatching a job
- Starting the daemon to process the queues
- Problems with processing on the main thread
- Jobs and Queues
- Jobs
- Queues
- Jobs vs Processing on the main thread
- Practical example: simulating several clients
- Queue drivers
- Creating a Job in Laravel
- Final benefits
Laravel, by allowing us to build all kinds of systems and perform all kinds of debugging, such as viewing SQL queries, from simple to complex, often faces tasks that require high computing power. This is one of the main disadvantages of web applications compared to desktop applications: web applications, usually (at least the traditional ones), have fewer available computing resources or are more limited.
This is noticeable when performing relatively simple operations, such as sending an email or processing an image. If these tasks are executed on the server's main thread (the same one that handles requests), the server may take several seconds to return a response. This additional time is due to the processing of these costly operations. Furthermore, if the operation exceeds the computing capacity or if many people perform these operations simultaneously, the server may generate a time exhaustion error.
Solution: delegating operations through queues
To solve this, we must use a mechanism that allows these processes to be executed efficiently. Instead of performing them on the main thread, we can delegate these tasks to secondary processes, which are responsible for executing them consecutively, one after the other. This is where queues and jobs come into play.
Jobs and queues
Jobs are operations that are costly in terms of computing: sending emails, processing images, handling Excel files, generating PDFs, etc. These jobs are assigned to queues, which are responsible for processing them efficiently. We can configure several secondary processes that work on the queues to execute these tasks without saturating the system.
Furthermore, it is possible to:
- Specify priorities for jobs so that some are executed before others.
- Increase processing capacity horizontally by adding more workers.
- Retry failed jobs, improving the system's fault tolerance.
Benefits
By using the queue and worker system, we get:
- Improved application responsiveness.
- Asynchronous execution of jobs, without affecting the main thread.
- Efficient processing of costly operations, even if there are multiple concurrent tasks.
Ultimately, queues allow web applications to handle heavy work reliably and scalably, preventing the server from being saturated and maintaining a fluid user experience.
So, having clarified the importance of this system, let's learn how to implement it.
Queue driver
First, we must choose a queue driver to use among all the existing ones:
- 'sync': The 'sync' driver executes queued jobs immediately after and within the same request-response cycle. It is suitable for development and testing environments, but not for production.
- 'database': The 'database' driver stores queued jobs in a database table in a separate queue worker process.
- 'redis': The 'redis' driver uses Redis as the queue store.
- 'beanstalkd': The 'beanstalkd' driver uses the Beanstalkd queue service to process queues.
- 'sqs' (Amazon Simple Queue Service): The 'sqs' driver is used for integration with Amazon SQS.
- 'rabbitmq': The 'rabbitmq' driver allows using RabbitMQ as the queue driver.
- 'null': The null driver is used to completely disable the queue system.
Configuration
By default, the database one is configured:
config\queue.php
'default' => env('QUEUE_CONNECTION', 'database')And to perform the following tests, you can use the database one, although Redis is usually an excellent option because it is a fast and efficient database that we installed previously and it is the one we will use:
config\queue.php
'default' => env('QUEUE_CONNECTION', 'redis')Finally, we start the process to execute the jobs using:
$ php artisan queue:work
In addition, Laravel allows us to retry failed jobs using the command:
$ php artisan queue:work --tries=3Here we indicate the number of attempts Laravel should make to execute a job again in case an error or exception occurs during its execution.
If a job throws an exception, it is considered a failed job and Laravel can manage it according to the configuration.
And we will see on the console:
INFO Processing jobs from the [default] queue. Every time a job is processed, you will see in the terminal:
.................................................................... 3s DONE 2026-07-12 09:44:31 App\Jobs\TestJob ............................................................................... RUNNING 2026-07-12 09:44:34 App\Jobs\TestJob ............................................................................... 3s DONE 2026-07-12 09:45:43 App\Jobs\TestJob ............................................................................... RUNNINGIt doesn't matter if you dispatch jobs from your controllers or similar and the queue is NOT active, Laravel registers them and when you activate the queue process, it dispatches them; and that's it, with this, Laravel starts a process to manage the jobs, we just need to create the job which we will see in the next section.
In this post, we will learn about the use of Queues and Jobs in Laravel to perform background tasks.
The idea of this API is that we can defer tasks (leave them in the background) that require high computing consumption and all this is to improve the user experience and prevent application crashes; in such a way that these high-compute tasks will be executed in another thread or process on our server and will be resolved as they arrive or depending on their priority.
Basically, this whole process is carried out between tasks/jobs (the high computing processes) and queues (the mechanism for registering the jobs).
Likewise, we are going to talk a little more in detail about these two fundamental components.
The jobs and the queues
Jobs are the heavy task or the ones that require a lot of processing that we want to defer; and they can be of any type such as mass or simple sending of emails, processing images, videos, etc.; therefore, these are the tasks that we register as pending for Laravel to process one by one as it becomes available; so the next thing you might ask is, How is this organization handled?; that is, who is responsible for managing the priority between tasks or jobs (queues) and how do we maintain an organization of all this.
The jobs
In Laravel, jobs are an important part of the framework's job queue functionality, that is, jobs are used to process the heavy tasks stored using "queues"; these heavy tasks can be anything like sending emails, processing images, videos, among others; this is ideal since these tasks are not part of the main user session and it avoids consuming resources from the main user session, besides avoiding the famous "not responding" windows and in general, you have a better experience in the use of the application. since it does not affect the performance or responsiveness of the main application.
Jobs in Laravel can be created to perform any task needed in the back-end without the user having to wait for the task to complete; that is, like sending an email or exporting data in an excel or similar format.
The great thing about jobs is that they are executed sequentially, suppose you create a job to process emails, the emails are sent one by one without the need to overload the page, which would not happen if the email sending was handled from the main user session and suddenly, 5 or more users came to send emails from the application, therefore, at the same instant of time, you would have multiple email sendings and consuming the resources that this requires.
In summary, jobs in Laravel are an essential part of the framework's job queue, and allow heavy or non-essential tasks to be performed in the background to improve the performance and responsiveness of the main application.
The queues
At this point, we have introduced the concept of "queues" in the use of jobs, in the same way, we are going to explain it quickly; the job queues correspond to the operation that is to be performed, which, as we commented before, corresponds to the "heavy" operation that is to be performed, that is, instead of sending an email from a controller, it is stored in a queue that is later processed by another process.
Create your own queues in Laravel to maintain order
The answer to what was previously commented is quite simple, it would be the queues as a mechanism that allows registering or adding these jobs; we can register or have as many queues as we want and we can specify to Laravel using artisan which queues we want to be processed first; for example, let's see the following example:
$ php artisan queue:work --queue=high,defaultUsing the previous command, we are telling Laravel to first process the jobs in a queue called "high" and then those in a queue called "default", which by the way, is the queue we have defined by default.
Connections for the structure to maintain the queues
Connections are the predetermined way we have to indicate how the entire queue process will be carried out (since remember that the queue absorbs the task, therefore, at this point, once the task or job is registered in a queue, the task no longer matters in our queue); but to be able to use this entire structure we must indicate the connection and with it we must indicate:
- The driver
- Default or configuration values
Therefore, through connections we can specify the driver we are going to use, which can be anything like the database or other services; for example:
Database:
- Amazon SQS: aws/aws-sdk-php ~3.0
- Beanstalkd: pda/pheanstalk ~4.0
- Redis: predis/predis ~1.0 or phpredis PHP extension
And some extra parameters for the configuration.
Configure the Queue Driver or connector for the database
Now we are going to open the config/queue.php file and the .env file, in which we are going to look for a configuration called QUEUE_CONNECTION
Which by default is called QUEUE_CONNECTION and has the sync configuration, if you analyze it a bit you will see that we have several types of connections that we can use, among them the one called database which is the one we are going to use, therefore configure it at least in the .env file; in my case I'm going to leave it like this in the .env:
QUEUE_CONNECTION=database And in config/queue.php it remains: 'default' => env('QUEUE_CONNECTION', 'database'),Create the jobs table in our database
Now, the next thing you can ask yourself is, how can we configure the table in our database?; following the official documentation:
$ php artisan queue:table php artisan migrateWe already have an artisan command that helps us in this whole process, which would be the one that allows us to generate the migration to create the table; and with this we are ready.
Creating our first Job in Laravel
Now we are going to create our first Job that we are going to configure with the default queue; for that, we are going to create one using our artisan:
$ php artisan make:job ProcessImageSmallAnd with this we have a file generated in App\Jobs
Which consists of two main blocks:
- __construct
- handle
The constructor function to initialize the basic data that we are going to use and the handle function to do the heavy process; as simple as that, therefore in the handle function we can send the emails, process videos or images, for our example we are going to assume that we have an image that we are receiving by parameters:
protected $image;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(PostImage $image)
{
$this->image = $image;
}This image is basically an instance of a model that looks like this:
class PostImage extends Model
{
protected $fillable = ['post_id', 'image'];
public function post(){
return $this->belongsTo(Post::class);
}
public function getImageUrl(){
return URL::asset('images/'.$this->image);
//return Storage::url($this->image);
}
}And now, we are going to do a heavy process like scaling an image; for this, we are going to use the following package that we can install using composer:
$ composer require intervention/imageThe code we are going to use basically explains itself:
<?php
namespace App\Jobs;
use App\PostImage;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Intervention\Image\ImageManagerStatic;
class ProcessImageSmall implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $image;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(PostImage $image)
{
$this->image = $image;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$image = $this->image;
$fullPath = public_path('images' . DIRECTORY_SEPARATOR . $image->image);
$fullPathSmall = public_path('images' . DIRECTORY_SEPARATOR . 'small' . DIRECTORY_SEPARATOR . $image->image);
$img = ImageManagerStatic::make($fullPath)->resize(300,200);
$img->save($fullPathSmall);
}
}In which we simply create an instance of our package, which we installed previously, specify the image location and using the resize function we scale the image to the specified dimensions; then, we save the new image and with this we are ready.
Dispatching a job
The next thing we have to specify is where we are going to call this job, which can be anywhere in our application, such as when we upload an image, etc.; suppose we have a method to upload an image in Laravel for example:
public function image(Request $request, Post $post)
{
$request->validate([
'image' => 'required|mimes:jpeg,bmp,png|max:10240', //10Mb
]);
$filename = time() . "." . $request->image->extension();
$request->image->move(public_path('images'), $filename);
//$path = $request->image->store('public/images');
$image = PostImage::create(['image' => /*$path*/ $filename, 'post_id' => $post->id]);
ProcessImageSmall::dispatch($image);
return back()->with('status', 'Imagen cargada con exito');
}From here; if you look closely at the code, you will see that we define an instance of our queue with the dispatch method to dispatch the job ProcessImageSmall::dispatch($image)
Starting the daemon to process the queues
Now if we check the previous table, we will see that a job was registered, but it has not been dispatched, to dispatch it, we have to activate the daemon that is responsible for this work, raising the process that is responsible for pending jobs in the jobs table:
$ php artisan queue:work
And with this we will see that everything is ready and the job has already been dispatched:

With this, we learned to use jobs and queues in Laravel; remember that this is one of the many topics that we cover in depth in our Laravel course that you can take on this platform in the courses section.
Problems with processing on the main thread
When we perform costly operations on the main thread, the server can take several seconds to respond. This occurs, for example, when:
- Processing images.
- Generating PDFs.
- Reading Excel files.
- Sending mass emails.
If many users execute these operations simultaneously, the server can become saturated, show 500 errors, or exceed maximum execution times. This affects the user experience and the system's stability.
Practical example: 20 boxes
- Imagine you have to transport 20 large boxes:
- If they are all given to you at the same time, it will be impossible to handle them without dropping them.
- If they are given to you one by one, you will be able to place them correctly at their destination.
The same thing happens with the server: processing costly tasks one by one using queues and jobs prevents saturation and memory errors.
Jobs and Queues
Jobs
Jobs are costly or complex tasks that require high computing power, such as:
- Sending emails.
- Processing images.
- Generating PDFs.
- Reading or manipulating Excel files.
Each job is a class that encapsulates a specific operation, with its parameters and internal logic. For example, you could have a job that receives a user and sends a welcome email.
Queues
Queues are the processes that manage the jobs. These are executed in secondary threads, asynchronously and sequentially, avoiding blocking the main thread.
- We can configure several secondary processes to process jobs in parallel if the server allows it.
- Jobs are processed one by one, preventing saturation and improving the system's fault tolerance.
- If a job fails, it can be automatically retried.
Jobs vs Processing on the main thread
Processing jobs on the main thread means that the application must handle everything at the same time. This causes:
- Slow responses to the user.
- Possible 500 errors if the server is saturated.
- Poor user experience.
With queues and jobs, tasks are executed sequentially or controllably:
- Jobs are added to the queue and processed one by one.
- The main thread continues to handle other requests without being blocked.
- User experience improves and the application is more stable.
Practical example: simulating several clients
Suppose three clients send simultaneous requests:
- If we process the tasks on the main thread, the server can become saturated or the operations take a long time.
- With queues, each job is queued and processed one by one, preventing saturation.
- This simulates a controlled flow, like receiving boxes one by one instead of all at the same time.
Queue drivers
Laravel offers several drivers to manage the queues:
- sync: executes the job synchronously on the main thread. Useful only in development or testing.
- database: stores the queue in the database. Simple and functional for testing.
- redis: very fast, recommended for production. Works with key-value databases.
- null: disables the queues.
The driver you use does not affect the implementation; it only changes the storage and processing mechanism for the tasks.
Creating a Job in Laravel
Create the job with Artisan:
$ php artisan make:job TestJobIn the generated class:
Define attributes and constructor if the job requires parameters.
Implement the handle() method with the operation's logic.
Simple example: a job that sleeps for 3 seconds to simulate a costly process:
public function handle()
{
Log::info('Before sleep');
sleep(3);
Log::info('After sleep');
}Dispatch the job from a controller or route:
TestJob::dispatch($parametro);Start the queue worker:
$ php artisan queue:work
The jobs will be executed in the secondary thread, without blocking the main application flow.
Final benefits
- Improves application responsiveness.
- Increases fault tolerance: costly jobs are not lost if the server crashes.
- Allows asynchronous processing of complex tasks.
- It is scalable: you can add more processes to process multiple jobs in parallel.
Another interesting development, since we have made a lot of progress with developments in Laravel, is learning how to have a multilingual app.
I agree to receive announcements of interest about this Blog.
In this post we are going to learn about the use of Queues and Jobs in Laravel (Queues and Jobs) to be able to perform jobs in the background.