All posts
Mautic email deliverability: replace SMTP with the Symfony Mailer DSN transport
Gabe 3 min read

Mautic email deliverability: replace SMTP with the Symfony Mailer DSN transport

Mautic 5 runs on Symfony Mailer, not SwiftMailer — which means it can take a native mailkite+api:// DSN instead of generic SMTP. This tutorial covers the install, the DSN config, and why campaign-send errors are more useful than an SMTP rejection code.

Mautic is a marketing-automation platform, so email is the product, not a side effect. A contact form on a CMS sends a handful of messages a day; a Mautic campaign to a real segment can fan out to thousands in one send. That makes Mautic’s mail transport the single highest-leverage setting in the whole install — and most Mautic docs still point you at generic SMTP.

Mautic 5 doesn’t need SMTP for this anymore. It moved from SwiftMailer to Symfony Mailer, which resolves transports from a DSN string, and MailKite ships a first-party transport for exactly that: mailkite/symfony-mailer. Install it, set one field, and Mautic’s send errors go from a numeric SMTP code to the API’s own explanation of what actually went wrong.

Why this beats pointing Mautic at SMTP

Generic SMTP gives you a status code and, if you’re lucky, a line of server text. It also means every send is a full protocol round-trip: connect, EHLO, AUTH, MAIL FROM, RCPT TO, DATA. The DSN transport skips the protocol layer and calls the MailKite API directly, and when something’s wrong — an unverified domain, a suppressed recipient, a rate limit — it says so:

Unable to send an email: Can't send yet — domain not verified. (MailKite API status 403).

Compare that to 550 5.7.1 Relaying denied and try to explain to a client why their newsletter didn’t go out.

Install the transport

composer require mailkite/symfony-mailer

Requires PHP 8.1+ and Symfony Mailer 6.4 or 7.x — both satisfied by any current Mautic 5 install.

Set the DSN

Get an API key from the MailKite dashboard, then set it as Mautic’s Mailer DSN. Where this lives depends on how your instance is configured — either .env.local (or wherever your host reads MAILER_DSN) or, on some Mautic installs, Configuration → Email Settings → Service: DSN:

MAILER_DSN=mailkite+api://mk_live_xxx@default

The API key sits in the DSN’s user slot — there’s no separate SMTP username to invent, and no password field to keep in sync with it. Mautic autoconfigures the transport factory the moment the package is installed; nothing else to register.

Clear Mautic’s cache after changing the DSN:

bin/console cache:clear

Verify your sending domain

The from address on every campaign has to be on a domain with SPF and DKIM published in MailKite — that’s true whether you’re sending through this transport or plain SMTP, and it’s the actual determinant of whether Mautic’s mail lands in the inbox or spam. See mailkite.dev/docs/quickstart if you haven’t set that up yet.

Test it

Create a test email in the Mautic editor and use Send test. Check the MailKite dashboard’s message log to confirm delivery — if it fails, the error you see in Mautic now comes straight from the API instead of a generic SMTP rejection.

No shell access? Fall back to SMTP

Some managed Mautic hosts don’t give you Composer access. In that case, the plain SMTP path still works — same domain, same API key as the password, just without the honest-error behavior described above:

SettingValue
SMTP Hostsmtp.mailkite.dev
SMTP Port587
EncryptionTLS (STARTTLS)
Usernamemailkite
PasswordYour API key (mk_live_…)

A note on campaign-scale sending

Symfony Mailer transports — this one included — send one email per request; that’s the contract every transport follows, not a MailKite-specific limitation. Mautic’s own campaign/batch layer is what turns a segment send into individual calls, so this doesn’t change how a campaign send behaves. If you’re calling the MailKite API directly, outside Mautic, for a very large one-off send, use the PHP SDK’s sendBatch() instead of looping individual sends.

Troubleshooting

  • DSN not picked up — clear the cache (bin/console cache:clear) after any change to MAILER_DSN.
  • “User is not set” — the API key has to be in the DSN’s user slot: mailkite+api://mk_live_xxx@default, not mailkite+api://default.
  • 535 Authentication failed (SMTP fallback path only) — the password field must be your mk_live_… API key, not a separate SMTP credential.
  • Campaign sends but individual test doesn’t (or vice versa) — check that the specific from address used matches a verified domain; Mautic allows setting different from addresses per campaign.

Full transport reference, the mapping table, and what the transport refuses rather than silently drops: mailkite.dev/docs/integrations/symfony-mailer. Platform-specific config reference: mailkite.dev/docs/integrations/mautic.

Discuss this post: Hacker News Share on X Share on LinkedIn

Related posts