Email is often the forgotten performance bottleneck in Magento 2. While stores obsess over page load times and database queries, the email subsystem quietly drags down checkout completion, order processing, and cron execution. A single slow SMTP handshake can add seconds to every order placement. Multiply that by hundreds of orders per hour and you have a real problem.
In this post we'll diagnose the most common email performance issues and implement fixes that actually move the needle.
The Hidden Cost of Synchronous Email
By default, Magento 2 sends emails synchronously during the request lifecycle. When a customer places an order, the system:
- Renders the email template (PHP + HTML compilation)
- Opens an SMTP connection
- Authenticates and transmits the message
- Waits for server acknowledgement
- Closes the connection
Steps 2-4 happen inside the same request that renders the order success page. If your SMTP server is 150ms away and takes 300ms to accept a message, every customer waits an extra 300-500ms after clicking "Place Order." During peak traffic this compounds into connection exhaustion and timeout errors.
The worst part? These delays rarely show up in standard profiling because they're classified as "network I/O" rather than Magento code execution.
Fix 1: Enable Async Email Sending
Magento 2.4+ includes built-in support for asynchronous email via RabbitMQ or MySQL message queues. This decouples email transmission from the frontend request.
Enable it in app/etc/env.php:
'system' => [
'default' => [
'sales_email' => [
'general' => [
'async_sending' => 1
]
]
]
]
Or via CLI for immediate effect:
bin/magento config:set sales_email/general/async_sending 1
bin/magento cache:clean config
With async sending enabled, order confirmation emails are queued immediately and processed by a background consumer. The customer sees the success page instantly while the email ships separately.
Verify the consumer is running:
bin/magento queue:consumers:start sales.sendOrderEmails
For production, register this as a systemd service or Supervisord process. Without a running consumer, emails accumulate in the queue indefinitely.
Fix 2: SMTP Connection Pooling with External Providers
Default Magento uses PHP's mail() function or opens a fresh SMTP connection per email. Both are inefficient at scale.
Switch to a transactional email service (Mailgun, SendGrid, AWS SES, Postmark) and configure connection reuse. Most support HTTP APIs that are faster than SMTP handshakes, but if you must use SMTP, enable persistent connections.
For SendGrid via SMTP in app/etc/env.php:
'system' => [
'default' => [
'system' => [
'smtp' => [
'host' => 'smtp.sendgrid.net',
'port' => 587,
'auth' => 'login',
'username' => 'apikey',
'password' => 'SG.your-api-key-here',
'ssl' => 'tls'
]
]
]
]
Better yet, use a Magento module that calls HTTP APIs directly. HTTP requests have lower overhead than SMTP connection negotiation and support keep-alive pooling natively.
Fix 3: Template Compilation Cache
Email templates in Magento 2 are PHP-based and compiled at runtime. The first time a template renders, Magento parses the .html file, extracts directives ({{var order.getIncrementId()}}), and generates executable PHP code.
This compilation is cached, but the cache is filesystem-based and can be slow on NFS or shared storage. Ensure template compilation cache is stored in Redis:
'cache' => [
'frontend' => [
'default' => [
'backend' => 'Magento\Framework\Cache\Backend\Redis',
'backend_options' => [
'server' => '127.0.0.1',
'port' => '6379',
'database' => 0,
'password' => '',
'compress_data' => 1,
'compression_lib' => 'gzip'
]
]
]
]
Warm the email template cache after deployments by triggering a test email or visiting the template preview in Admin > Marketing > Email Templates.
Fix 4: Batch Processing for Bulk Operations
Bulk actions like invoice generation, shipment creation, or newsletter blasts trigger dozens of emails in rapid succession. Each email in isolation is fast; together they overwhelm the mail subsystem.
For newsletter sends or promotional campaigns, avoid Magento's native email loop. Instead, export recipient lists to your transactional email provider's batch API. SendGrid, Mailgun, and SES all support batch endpoints that accept thousands of recipients in a single request.
For operational emails (invoices, shipments), implement a custom batch queue:
class BatchEmailSender
{
private $batch = [];
private $batchSize = 50;
public function queueEmail($templateId, $vars, $to)
{
$this->batch[] = compact('templateId', 'vars', 'to');
if (count($this->batch) >= $this->batchSize) {
$this->flush();
}
}
public function flush()
{
if (empty($this->batch)) {
return;
}
// Send via provider's batch API or grouped SMTP transaction
foreach ($this->batch as $email) {
// Process
}
$this->batch = [];
}
}
Hook this into invoice and shipment mass-action controllers to reduce SMTP round-trips by 50x.
Fix 5: Eliminate Unnecessary Emails
Every module that hooks into sales_order_place_after or checkout_submit_all_after can trigger emails. Audit your observers:
grep -r "transportBuilder" app/code/ vendor/ --include="*.php" | grep -v Test
For each match, ask: does this email need to send immediately? Can it be deferred? Can it be suppressed entirely?
Common offenders:
- Third-party review request modules — sending 5 minutes post-purchase via cron. Move to daily batch.
- Inventory update notifications — admins don't need real-time stock alerts. Digest them hourly.
- Duplicate order confirmations — some payment modules send their own confirmation alongside Magento's. Disable one.
Fix 6: Monitor the Email Queue
You can't optimize what you don't measure. Add queue depth monitoring to your observability stack:
# Check RabbitMQ queue depth (if using RabbitMQ)
rabbitmqctl list_queues name messages | grep sales
# Check MySQL queue (if using db queue)
mysql -e "SELECT COUNT(*) FROM queue_message WHERE queue_id = (SELECT id FROM queue WHERE name = 'sales.sendOrderEmails')"
Alert when queue depth exceeds 100 messages or oldest message age exceeds 5 minutes. These thresholds catch consumer failures before customers complain about missing order emails.
For SMTP-level monitoring, track these metrics per email provider:
- Delivery rate (should be >97%)
- Bounce rate (keep <2%)
- Average send latency (target <500ms from queue to acceptance)
- Daily volume vs. rate limits
Fix 7: Tune Cron for Email-Heavy Jobs
Magento's cron handles email consumers, reminder sends, and report generation. On email-heavy stores, dedicate a separate cron group:
<!-- app/etc/cron_groups.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<group id="email">
<schedule_generate_every>1</schedule_generate_every>
<schedule_ahead_for>4</schedule_ahead_for>
<schedule_lifetime>2</schedule_lifetime>
<history_cleanup_every>10</history_cleanup_every>
<history_success_lifetime>60</history_success_lifetime>
<history_failure_lifetime>600</history_failure_lifetime>
<use_separate_process>1</use_separate_process>
</group>
</config>
Register email consumers under this group to prevent email backlog from blocking catalog price rule updates or indexer runs.
Summary
Email performance is request-blocking, queue-dependent, and often invisible until it breaks. The fixes are straightforward but require changing default behaviors:
| Issue | Fix | Impact |
|---|---|---|
| Synchronous SMTP in checkout | Enable async sending | -300-500ms per order |
| Fresh SMTP connection per email | Connection pooling / HTTP API | -200ms per email |
| Template compilation overhead | Redis cache + pre-warm | -50ms first render |
| Bulk email loops | Batch processing | 10-50x throughput gain |
| Unnecessary email triggers | Audit & suppress observers | Reduced queue pressure |
| Consumer failures | Queue depth monitoring | Prevent silent email loss |
| Cron contention | Dedicated email cron group | Isolated email processing |
Start with async sending — it's a single configuration change with immediate customer-facing impact. Then layer in SMTP optimization and monitoring. Your checkout completion rate will thank you.
Have you diagnosed email bottlenecks in your Magento store? Share your SMTP provider and latency numbers — the community needs more real-world benchmarks.
Top comments (0)