Sending emails is a common task in web applications. Whether it’s a welcome email after registration, password reset link, or order confirmation, email notifications play a big role in improving user engagement.

But sending emails directly from your controller can slow down your app, especially if you have thousands of users. Laravel solves this with Jobs and Queues, making email sending much faster and more efficient.

In this blog, we’ll walk through a real-life example of sending emails using Laravel Jobs and Queues — step-by-step, in plain language, and with code examples.

Why Use Jobs and Queues for Email?

Before we dive into code, let’s understand the problem.

Imagine you have a registration form, and after the user signs up, you send them a welcome email. If you send the email immediately inside the controller, the user will have to wait for the email to be sent before the page loads.

That’s not a good user experience

With Laravel’s queue system, you can:

  • Offload time-consuming tasks (like sending emails) to a background worker

  • Speed up the response time for users

  • Scale easily as your app grows

So, let's see how you can implement this.

Prerequisites

Before starting, make sure:

  • You have Laravel installed (Laravel 10 or above is recommended)

  • A mail driver is configured in your .env file (e.g., Mailtrap for testing or SMTP for production)

  • Queue driver is set (we’ll use the database driver here)

Step 1: Configure Mail Settings 

Open your .env file and set up the mail configuration. Here's an example using Mailtrap:

MAIL_MAILER=smtp
MAIL_HOST=sandbox.smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=your_username
MAIL_PASSWORD=your_password
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=hello@example.com
MAIL_FROM_NAME="MyApp"
 

Test it first by sending a test email using Laravel’s Mail::to() method to make sure your mail setup is working.

Step 2: Create the Mailable Class

Laravel provides an easy way to define how your email should look. You can generate a mailable class using Artisan:

php artisan make:mail WelcomeUserMail
 

Then go to app/Mail/WelcomeUserMail.php and modify it like this:

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class WelcomeUserMail extends Mailable
{
    use Queueable, SerializesModels;

    public $user;

    public function __construct($user)
    {
        $this->user = $user;
    }

    public function build()
    {
        return $this->subject('Welcome to MyApp!')
                    ->view('emails.welcome');
    }
}
 

Create the view file:
resources/views/emails/welcome.blade.php

 
<!DOCTYPE html>
<html>
<head>
    <title>Welcome to MyApp</title>
</head>
<body>
    <h1>Hello, {{ $user->name }}!</h1>
    <p>Thank you for registering at MyApp. We're glad to have you.</p>
</body>
</html>

Step 3: Set Up the Queue Configuration

Now let’s enable the database queue driver. In your .env file:

QUEUE_CONNECTION=database
 

Then run the following commands to set up the necessary database tables:  

php artisan queue:table
php artisan migrate
 

This will create a jobs table in your database, where queued jobs will be stored.

Step 4: Create the Job to Send Email

Now we’ll create a job class that handles the task of sending the email.

Run this command:

php artisan make:job SendWelcomeEmail
 

Now open app/Jobs/SendWelcomeEmail.php and update it like this:

namespace App\Jobs;

use App\Mail\WelcomeUserMail;
use Illuminate\Bus\Queueable;
use Illuminate\Support\Facades\Mail;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

class SendWelcomeEmail implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public $user;

    public function __construct($user)
    {
        $this->user = $user;
    }

    public function handle()
    {
        Mail::to($this->user->email)->send(new WelcomeUserMail($this->user));
    }
}
 

Notice how we implement ShouldQueue, which tells Laravel this job should be queued.

Step 5: Dispatch the Job

Now it’s time to trigger the job when a user registers. You can dispatch it like this:

use App\Jobs\SendWelcomeEmail;

// Inside your registration controller
public function register(Request $request)
{
    $user = User::create([
        'name' => $request->name,
        'email' => $request->email,
        'password' => bcrypt($request->password),
    ]);

    // Dispatch the job
    SendWelcomeEmail::dispatch($user);

    return response()->json(['message' => 'User registered successfully!']);
}
 

Step 6: Run the Queue Worker

To actually process the jobs, you need to run a queue worker. In your terminal, run:

php artisan queue:work
 

Keep this running in a terminal window. This worker will listen for new jobs and execute them.

You’ll see something like this when a job is processed:

[2025-05-24 12:00:00] Processing: App\Jobs\SendWelcomeEmail
[2025-05-24 12:00:01] Processed:  App\Jobs\SendWelcomeEmail

 

Handle Failures Gracefully

Sometimes jobs fail due to network issues or invalid email addresses. You can monitor failed jobs by enabling the failed_jobs table:

php artisan queue:failed-table
php artisan migrate
 

Then, failed jobs will be stored in this table. You can retry them using:

php artisan queue:retry all
 

Or just retry a specific job by ID:

php artisan queue:retry 5
 

You can also customize the failed() method inside your job class to log errors or notify an admin.


Final Thoughts

Using Jobs and Queues in Laravel is the best practice for handling emails (and other time-consuming tasks) without slowing down your application.

Here’s a quick recap:

✅ Jobs help you separate logic
✅ Queues improve performance
✅ Laravel makes it all easy with built-in support

This method is scalable, clean, and reliable. Whether you're building a blog, an e-commerce store, or a SaaS product, you should use queues for background tasks like email, reports, or heavy processing.