Laravel SMTP lets a Laravel application send transactional messages through an SMTP relay without adding provider-specific application code. This guide configures Laravel’s built-in SMTP transport for Volanea, reads the mail secret from an environment variable, and sends a complete transactional email.
What you will build
By the end of this guide, a Laravel application will have:
- Laravel’s SMTP mailer selected as the active transport.
- Volanea SMTP connection details stored outside source control.
- A mail secret loaded from
VOLANEA_API_KEYrather than hard-coded in PHP. - A
WelcomeEmailMailable with a Blade HTML view and a plain-text fallback. - A protected application route that validates input and sends one transactional message.
- A repeatable process for testing, queueing, and troubleshooting delivery.
This approach uses standard SMTP rather than a fictional provider-specific Laravel package. That matters because the code remains portable: Laravel renders the message, Symfony Mailer opens the SMTP connection, and Volanea accepts the message through the relay configuration issued for your account.
Before continuing, have these values available from your Volanea account configuration:
- The SMTP hostname.
- The SMTP port.
- The SMTP username, if your relay credentials include one.
- The SMTP secret or API key used as the relay password.
- A sender address that your Volanea account is authorized to send from.
Do not guess the hostname, port, encryption setting, or username. SMTP providers can support multiple connection modes, and using an unverified setting is a common cause of authentication and TLS failures.
Requirements and installation
This guide assumes a current Laravel application using the framework’s standard mail configuration. Laravel uses Symfony Mailer for mail transports, including SMTP. Most Laravel installations already include the required mailer package, but run the following command to explicitly install or update the SMTP mailer dependency:
composer require symfony/mailer
You also need a Laravel application and a working PHP environment. If you are starting from a new project, create the application first and then install the mailer dependency:
composer create-project laravel/laravel volanea-laravel-smtp
cd volanea-laravel-smtp
composer require symfony/mailer
Laravel’s default config/mail.php file already includes an SMTP mailer definition in typical installations. The important work is selecting that mailer and supplying the connection data through environment variables.
Avoid placing credentials in a controller, a Mailable class, a committed configuration file, or a frontend bundle. SMTP credentials provide sending access. Treat them like database passwords: keep them in local environment files during development and in your deployment platform’s encrypted environment-variable store in production.
Configure Volanea SMTP environment variables
Add the following values to your application’s .env file. Replace placeholder values with the exact SMTP settings and sender address issued for your Volanea account.
MAIL_MAILER=smtp
MAIL_HOST=your-volanea-smtp-host
MAIL_PORT=your-volanea-smtp-port
MAIL_USERNAME=your-volanea-smtp-username
MAIL_ENCRYPTION=tls
VOLANEA_API_KEY=your-volanea-smtp-secret-or-api-key
MAIL_FROM_ADDRESS=notifications@your-verified-domain.example
MAIL_FROM_NAME="Example App"
The key part of this configuration is that the secret lives in VOLANEA_API_KEY. Laravel’s SMTP transport expects a password field, so the mail configuration will read that environment variable and pass it to the SMTP transport as the password.
Use the exact authentication value Volanea provides for SMTP. Some SMTP configurations use an API key as the SMTP password; others issue a dedicated SMTP password. If your account has a dedicated SMTP password rather than an API key, keep the same configuration structure but name the environment variable according to your secret-management convention, such as VOLANEA_SMTP_PASSWORD. Do not substitute a REST API credential unless the SMTP credential instructions for your account explicitly identify it as valid for relay authentication.
The MAIL_FROM_ADDRESS must be a sender address your account is permitted to use. A valid-looking address is not enough: the sending domain must be configured according to the requirements for your Volanea account. Sending with an unapproved address can lead to relay rejection or messages that do not authenticate as expected.
Choose the correct encryption value
Laravel’s SMTP configuration uses an encryption setting that must agree with the selected SMTP port and the relay’s supported TLS mode. tls is a common choice for an SMTP submission connection that upgrades using STARTTLS, but it is not universal.
Set MAIL_ENCRYPTION to the exact value required by the Volanea SMTP settings. If the documented connection mode does not use encryption, use null only when the provider explicitly supports that configuration and the connection is appropriate for your environment. Do not disable encryption merely to make a local connection error disappear.
When using a .env file, represent an intentionally empty value as follows:
MAIL_ENCRYPTION=null
In production, prefer a TLS-enabled submission configuration whenever the relay supports it. SMTP credentials should not travel over an unencrypted network connection.
Point Laravel’s SMTP password at the environment secret
Open config/mail.php and confirm that the SMTP mailer reads the Volanea environment variables. In many Laravel projects, the SMTP block already exists. Update it so the password reads from VOLANEA_API_KEY.
'smtp' => [
'transport' => 'smtp',
'url' => env('MAIL_URL'),
'host' => env('MAIL_HOST', '127.0.0.1'),
'port' => env('MAIL_PORT', 2525),
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
'username' => env('MAIL_USERNAME'),
'password' => env('VOLANEA_API_KEY'),
'timeout' => null,
'local_domain' => env(
'MAIL_EHLO_DOMAIN',
parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)
),
],
Also confirm that the default mailer is controlled by MAIL_MAILER:
'default' => env('MAIL_MAILER', 'log'),
With MAIL_MAILER=smtp in .env, Laravel uses the smtp configuration array when the application calls Mail::send().
If your deployment uses cached configuration, changing .env alone is not sufficient. Laravel may continue using the old cached values until configuration is rebuilt. Run these commands during deployment after the environment variables are available:
php artisan config:clear
php artisan config:cache
Do not run config:cache before the production environment variables are present. Cached configuration captures resolved values, so a missing secret at cache-build time can leave the worker or web process with an empty SMTP password.
Create the transactional Mailable
Generate a Mailable class and its Blade template:
php artisan make:mail WelcomeEmail
Replace the generated app/Mail/WelcomeEmail.php file with this complete class:
<?php
declare(strict_types=1);
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class WelcomeEmail extends Mailable
{
use Queueable;
use SerializesModels;
public function __construct(
public readonly string $recipientName,
) {
}
public function envelope(): Envelope
{
return new Envelope(
subject: 'Welcome to Example App',
);
}
public function content(): Content
{
return new Content(
view: 'emails.welcome',
text: 'emails.welcome-text',
);
}
public function attachments(): array
{
return [];
}
}
This class separates message metadata from message content. The Envelope supplies the subject, while Content identifies both the HTML and text views. Sending both versions is a practical transactional-email default: capable inboxes can display the HTML design, while text-only clients and certain automated systems have a readable alternative.
Create the HTML template at resources/views/emails/welcome.blade.php:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Welcome to Example App</title>
</head>
<body style="margin:0; padding:24px; background:#f4f4f5; color:#18181b; font-family:Arial, sans-serif;">
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
<tr>
<td align="center">
<table role="presentation" width="600" cellspacing="0" cellpadding="0" border="0" style="max-width:600px; width:100%; background:#ffffff; border-radius:8px;">
<tr>
<td style="padding:32px;">
<h1 style="margin:0 0 16px; font-size:24px;">Welcome, {{ $recipientName }}</h1>
<p style="margin:0 0 16px; line-height:1.5;">
Your Example App account is ready to use.
</p>
<p style="margin:0; line-height:1.5;">
If you did not create this account, you can safely ignore this email.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
Then create the plain-text alternative at resources/views/emails/welcome-text.blade.php:
Welcome, {{ $recipientName }}
Your Example App account is ready to use.
If you did not create this account, you can safely ignore this email.
Keep the first production message deliberately simple. A welcome message verifies the full path: Laravel rendering, SMTP authentication, relay acceptance, sender authorization, recipient delivery, and inbox display. It is easier to diagnose a connection issue with a small email than with a large template containing attachments, dynamic images, and multiple conditional blocks.
Add a route and controller that send one email
Generate a controller:
php artisan make:controller SendWelcomeEmailController
Replace app/Http/Controllers/SendWelcomeEmailController.php with the following code:
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Mail\WelcomeEmail;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
class SendWelcomeEmailController extends Controller
{
public function __invoke(Request $request): JsonResponse
{
$validated = $request->validate([
'email' => ['required', 'email:rfc,dns', 'max:254'],
'name' => ['required', 'string', 'max:100'],
]);
Mail::to($validated['email'])->send(
new WelcomeEmail($validated['name'])
);
return response()->json([
'message' => 'Welcome email accepted for sending.',
], 202);
}
}
Add the route in routes/web.php:
<?php
use App\Http\Controllers\SendWelcomeEmailController;
use Illuminate\Support\Facades\Route;
Route::post('/send-welcome-email', SendWelcomeEmailController::class);
This endpoint accepts a name and email address, validates both values, creates the Mailable, and asks Laravel to send it through the configured SMTP transport. The 202 Accepted response communicates that the application accepted the request for processing; it does not prove that the recipient has received or opened the email.
For a real application, do not expose this route publicly without authentication, authorization, rate limiting, and business logic that determines when a welcome message should be sent. A public endpoint that can send arbitrary email is an abuse risk and can damage sender reputation.
Run a local send test
Start the Laravel development server:
php artisan serve
In a second terminal, send a request to the route. Replace the URL and recipient address as needed:
curl -X POST http://127.0.0.1:8000/send-welcome-email \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"email":"recipient@example.com","name":"Taylor"}'
A successful application response looks like this:
{
"message": "Welcome email accepted for sending."
}
That response only confirms Laravel did not throw an exception while handling the request. Check the recipient inbox and spam folder, then inspect your Volanea sending activity and application logs as appropriate for your environment. If Laravel raises an SMTP exception, use the error text as the starting point for the troubleshooting section below.
For initial testing, send to an inbox you control. Avoid testing against customer addresses, purchased lists, or a large internal distribution list. A single controlled mailbox makes it easier to separate configuration issues from recipient-side policies, forwarding rules, or mailbox-provider filtering.
Understand synchronous sending and queues
The example calls Mail::send(). In a normal Laravel HTTP request, that operation is synchronous: the application waits while it renders the message, connects to the SMTP relay, authenticates, and submits the message. This is useful for a minimal verification path because errors are returned immediately to the application.
For production traffic, especially password resets, receipts, notifications, and signup flows at scale, use Laravel queues. Queueing shortens web request latency and gives you a controlled retry mechanism when a transient SMTP connection fails.
Update the Mailable to implement ShouldQueue:
<?php
declare(strict_types=1);
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class WelcomeEmail extends Mailable implements ShouldQueue
{
use Queueable;
use SerializesModels;
public function __construct(
public readonly string $recipientName,
) {
}
public function envelope(): Envelope
{
return new Envelope(subject: 'Welcome to Example App');
}
public function content(): Content
{
return new Content(
view: 'emails.welcome',
text: 'emails.welcome-text',
);
}
public function attachments(): array
{
return [];
}
}
Then dispatch it from the controller:
Mail::to($validated['email'])->queue(
new WelcomeEmail($validated['name'])
);
A queue worker must be running for queued email to leave your application:
php artisan queue:work
Do not confuse Laravel queueing with JavaScript async and await. PHP Laravel code does not use await around Mail::send() or Mail::queue(). If you queue a Mailable but do not run a worker, the job remains pending and no message is submitted to SMTP. Conversely, calling send() inside a slow HTTP endpoint can make users wait for the SMTP transaction.
Production configuration and deliverability considerations
SMTP configuration is only one part of transactional email delivery. A relay can accept a message while the receiving mailbox later filters, rejects, or defers it. Build operational practices around the entire sending path.
Use a stable sender identity
Keep transactional messages on a consistent sender address or sender subdomain where possible. Changing sender identities frequently makes it harder for recipients to recognize messages and harder for your team to investigate authentication or reputation issues.
Make the visible sender name clear and consistent with the product. Avoid vague names such as “Support Team” when the application has a recognizable brand name. For account-critical email, users should immediately understand why they received the message and what action, if any, they need to take.
Include a useful text version
The sample includes a text template for a reason. HTML email support varies widely across inboxes and security tools. A concise text alternative helps with accessibility, debugging, and clients that do not render HTML.
Do not use HTML as a way to hide essential content behind images or complicated layouts. For a password reset, receipt, or verification message, the key information should be readable as text and should remain understandable if CSS is removed.
Keep secrets out of logs
Avoid logging MAIL_PASSWORD, VOLANEA_API_KEY, full SMTP DSNs, or raw authorization configuration. Error logs should help diagnose connection failures without becoming a second credential store.
When debugging, log non-secret context such as the selected mailer name, host, port, environment name, queue job ID, and exception class. Restrict production log access and rotate credentials if a secret is accidentally exposed.
Validate recipients at the right point
The route example uses Laravel’s email:rfc,dns validation rule to reject malformed or obviously invalid domains before a send attempt. This is useful input validation, but it is not a guarantee that a mailbox exists or can receive mail.
For signup and list-import workflows, combine application validation with a deliberate address-quality strategy. You can use the free email address verification tool when reviewing individual addresses, while keeping permission and suppression handling in your application’s sending workflow.
Common errors
Authentication failed or 535 SMTP errors
An authentication error usually means the SMTP relay rejected the username, password, or both. Check each of these items before changing code:
- Confirm
MAIL_USERNAMEmatches the SMTP username issued for your account. - Confirm
VOLANEA_API_KEYis present in the runtime environment, not only in your local.envfile. - Confirm
config/mail.phpreadsenv('VOLANEA_API_KEY')for the SMTP password. - Rebuild Laravel’s config cache after changing production environment variables.
- Verify that the secret is valid for SMTP relay authentication, not merely for another API surface.
A frequent deployment mistake is setting VOLANEA_API_KEY in a web process but not in the queue worker process. The web route can appear configured while queued emails fail with authentication errors. Ensure every process that sends mail receives the same required environment variables.
Connection refused, timeout, or unreachable host
These errors occur before authentication. They usually indicate a bad host or port, a network egress restriction, a firewall rule, or an unavailable relay endpoint.
Do not change ports at random. Compare MAIL_HOST, MAIL_PORT, and MAIL_ENCRYPTION against the connection values issued for your account. In hosted environments, confirm that outbound connections to the selected SMTP port are allowed. Some hosting platforms restrict SMTP egress or require an approved relay configuration.
If the error happens only in a queue worker, compare worker networking and environment variables with the web application. Containers, serverless jobs, and background workers can have different network policies from the process that serves HTTP requests.
TLS handshake or certificate errors
TLS errors often mean the encryption mode and port do not match. For example, a connection expecting STARTTLS can fail when configured as an implicit TLS connection, and the reverse is also true.
Use the encryption method specified for your SMTP endpoint. Do not disable certificate verification in production to bypass a handshake failure. That hides the root problem and can expose credentials to an untrusted endpoint.
Also check the system clock on the host running Laravel. Incorrect time can cause certificate validation failures because certificates have validity windows.
Wrong content type when testing the route
The example endpoint expects JSON. If a test client sends form data or plain text while claiming it is JSON, Laravel may reject the request or fail validation because it cannot parse the expected fields.
Use both headers in the curl example:
-H "Content-Type: application/json" \
-H "Accept: application/json"
Content-Type describes the request body you are sending. Accept tells Laravel that you expect a JSON response. If you submit the route from an HTML form instead, use standard form encoding and adjust the frontend request accordingly.
Mail::queue() succeeds but no email is sent
Queueing only creates a job. It does not send the email until a queue worker processes that job. Verify the queue connection, database or broker configuration, worker process, failed-job storage, and worker logs.
For local testing, run:
php artisan queue:work
For production, run workers under a process manager or your platform’s managed queue service. Restart workers after deploying changes that affect the Mailable, configuration, or environment variables.
Async or await confusion
Laravel SMTP mail is PHP server-side code, not a browser-side JavaScript promise. Do not add await to PHP mail calls. Use Mail::send() for synchronous submission or Mail::queue() with a running worker for asynchronous background processing.
If a JavaScript frontend calls the Laravel endpoint, await belongs in the JavaScript HTTP request, not around SMTP configuration. The frontend should wait for the API response, while Laravel owns the actual send or queue operation.
Sender address rejected
A relay can reject a message when MAIL_FROM_ADDRESS is not authorized for the configured account. Confirm that the address and its domain are configured for sending in Volanea before changing the Mailable.
Do not “fix” this by replacing the sender with a random free-mail address. That can create authentication and alignment problems. Use a sender address belonging to a domain your organization controls and has configured for transactional sending.
Next steps
After the basic Laravel SMTP send works, move from a single welcome email to an operational sending workflow.
First, adopt reusable templates for messages that share a consistent structure, such as receipts, invitations, verification notices, and account alerts. Templates help teams keep content changes separate from delivery code, reduce copy-and-paste HTML, and make it easier to standardize text alternatives and sender presentation. Review the available request formats and setup material in the email API reference and setup guides when deciding whether a future template workflow should remain SMTP-based or use the REST API.
Second, plan for webhooks. Webhooks let an email platform notify your application about delivery-related events so you can record status changes, investigate bounces, respond to complaints, and avoid repeatedly sending to addresses that should no longer receive mail. Treat webhook endpoints as security-sensitive: verify requests according to the platform’s signing guidance, store event identifiers for deduplication, and return a fast successful response before doing longer processing in a queue.
Finally, add tests around your mail behavior. Laravel can fake the mailer during automated tests, allowing you to assert that the correct Mailable was addressed to the correct recipient without opening an SMTP connection. Keep one controlled integration environment for real SMTP verification as well, because fake-mail tests cannot prove credentials, network access, TLS, or sender authorization.
FAQ
Do I need a Volanea-specific Laravel SDK for Laravel SMTP?
No. Laravel’s SMTP mailer uses the standard SMTP protocol through Symfony Mailer. Configure the relay settings in environment variables and use Laravel’s built-in Mail facade and Mailable classes.
Where should I store the Volanea API key or SMTP secret?
Store it in VOLANEA_API_KEY or another server-side environment variable, then reference it from config/mail.php. Never place it in a Blade template, JavaScript bundle, mobile app, or committed source file.
Is Mail::send() synchronous?
Yes. In the typical Laravel request lifecycle, Mail::send() submits the message synchronously and can raise an SMTP exception during the request. Use Mail::queue() and a running queue worker when email delivery should happen in the background.
Why did Laravel return success but the recipient did not see the email?
A successful Laravel response means the application accepted or submitted the message; it does not guarantee inbox placement. Check spam or junk folders, sender authorization, relay activity, recipient address quality, and any delivery events available for your account.
Can I use this configuration for password-reset and receipt emails?
Yes. SMTP is appropriate for transactional email such as password resets, receipts, verification links, alerts, and invitations. Keep those messages concise, send from an authorized domain, include a text alternative, and queue them when request latency or send volume requires it.