Email is one of those Drupal jobs that looks simple until it costs you leads or support time. Password resets, contact forms, quote requests, approvals, receipts, and internal alerts all rely on the same pipeline. If that pipeline is brittle, the site may appear to work while messages quietly land in spam or never leave the server at all.
In current Drupal 10 and 11 projects, the right question is not just “how do I send mail?” It is “how do I make sure the right mail is composed clearly, delivered reliably, and easy to maintain later?” That is where many builds go wrong.
Start with the right split: Drupal composes, your mail backend delivers
Drupal core gives you a consistent mail API. In custom code, the normal pattern is to let plugin.manager.mail send messages, while your module defines subject lines and body text in hook_mail(). That separation matters because it keeps business logic out of templates and makes it easier to adjust delivery later.
For many teams, the important operational detail is this: a successful handoff inside Drupal does not guarantee inbox delivery. The API confirms that the message was accepted at PHP level, not that a recipient provider accepted it. That is why “the form submitted fine” is not the same as “the customer got the email.”
Choose the simplest mail path that is good enough
If your site only sends low-risk, low-volume notifications and your hosting stack already has a dependable mail transfer agent, core may be enough. But business-facing sites usually need a clearer setup.
- Use an SMTP backend when you want a straightforward, authenticated relay through Microsoft 365, Google Workspace, SendGrid SMTP, Mailgun SMTP, or another mail server. Drupal’s SMTP Authentication Support module exists specifically to bypass PHP’s native
mail()function and send directly to an SMTP server. - Consider the core Symfony mailer backend carefully if you want DSN-based transport configuration inside modern Drupal. Core includes an experimental
symfony_mailerbackend that can replace the default backend and point mail at an SMTP or sendmail transport. - Step up to a fuller mail system only when you genuinely need HTML email theming, attachments, async sending, provider APIs, or transport failover. That is a larger architecture choice, not a checkbox.
For a simple SMTP setup, this is still a sensible install path:
composer require drupal/smtpIf you want to test the experimental core Symfony mailer backend, the current Drupal API example uses settings.php overrides like this:
$config['system.mail']['interface'] = [
'default' => 'symfony_mailer',
];
$config['system.mail']['mailer_dsn'] = [
'scheme' => 'smtp',
'host' => 'smtp.example.com',
'port' => 25,
'user' => 'user',
'password' => 'pass',
'options' => [],
];The practical caveat: core’s Symfony backend is best seen as a transport replacement inside Drupal’s existing mail model. If your real goal is polished HTML newsletters, branded transactional layouts, or complex provider features, you will likely outgrow it quickly.
Keep custom mail code boring
The safest Drupal mail code is usually the least clever. Call the mail manager with a module name, a mail key, a recipient, a language, and a small set of parameters. Then let hook_mail() build the subject and body.
$mailManager = \Drupal::service('plugin.manager.mail');
$params = [
'customer_name' => $customerName,
'project_name' => $projectName,
];
$result = $mailManager->mail(
'my_module',
'project_notice',
$to,
$langcode,
$params
);In your module:
function my_module_mail($key, &$message, $params) {
if ($key === 'project_notice') {
$message['subject'] = 'Project update';
$message['body'][] = 'Hello ' . $params['customer_name'] . ',';
$message['body'][] = 'Your project “' . $params['project_name'] . '” has moved to the next step.';
}
}This pattern is easy to review, easy to test, and easy to localize. It also keeps you aligned with how Drupal expects mail to be composed. Use hook_mail_alter() sparingly for truly shared behavior, such as a footer or a reply-to rule. Do not hide core business rules there unless you enjoy future debugging.
Deliverability is now a first-class requirement
Even a well-written Drupal mail implementation can underperform if your sending domain is weakly configured. Google’s current sender rules make this explicit: all senders should publish SPF or DKIM, and bulk senders should publish SPF, DKIM, and DMARC. Google also recommends TLS, valid forward and reverse DNS, and consistent sender identity.
For operations teams, that means Drupal configuration is only half the work. You also need to verify that:
- The domain in your visible
From:address is the domain you actually authenticate. - Your SMTP or email provider is authorized in SPF and, ideally, signing with DKIM.
- You are not mixing marketing mail and transactional mail under the same vague sender identity.
- You can see bounce, rejection, or spam-rate signals outside Drupal itself.
If quote requests, application confirmations, invoices, or account emails matter to the business, treat mail like infrastructure. Test it after launches, after DNS changes, after provider changes, and after any redesign that changes templates or headers.
What I usually recommend
For most business sites, start with authenticated SMTP or a transactional mail provider, keep Drupal’s sending code simple, and avoid over-engineering HTML email until there is a real business case. If you later need richer branded templates or provider-specific features, move to a dedicated mail system deliberately instead of layering hacks onto a basic setup.
That approach costs less to support, creates fewer silent failures, and gives your team a cleaner path from “the contact form sends email” to “our site handles customer communication reliably.” If you want a second pair of eyes on a Drupal mail flow, delivery issue, or modernization plan, Greg can help audit the setup and tighten the weak points before they turn into missed enquiries.
Need help with this kind of work?
Talk to Greg about fixing or modernizing your Drupal email setup Get in touch with Greg.