symfony/google-mailer
Official Symfony Mailer bridge for sending email through Gmail and Google Workspace via the Gmail API. Configure OAuth2 credentials and use it as a Symfony Mailer transport for Google-backed delivery.
What the package actually is
symfony/google-mailer is a thin transport bridge, not a mailer and not a Laravel package. It supplies GmailTransport (plus a transport factory) that lets Symfony Mailer deliver messages through the Gmail REST API using a Google\Client instance. It does not provide: OAuth consent flow, token storage/refresh, retry policy, bounce/complaint handling, templating, or queueing. Those remain our responsibility.
Architecture fit
symfony/mailer, so the underlying integration point exists. There is no first-party Laravel gmail driver; we must register a custom transport via Mail::extend() and construct GmailTransport ourselves.Mail::mailer('gmail') routing, and per-tenant mailer configuration.Integration feasibility Technically straightforward: one service provider, one config block, one credential source. Operationally non-trivial: the credential lifecycle (OAuth client, refresh tokens, revocation, scopes) and Google's quota/verification regime dominate the effort, not the code.
Technical risk
| Risk | Severity | Notes / mitigation |
|---|---|---|
OAuth restricted scope (gmail.send) |
High | Publishing beyond test mode requires Google verification; restricted-scope apps storing data may require an annual CASA security assessment. Calendar time and cost, not engineering. |
| Refresh-token lifecycle | High | Testing-mode consent screens issue short-lived refresh tokens; revoked on password change/admin policy/inactivity. Needs encrypted persistence, refresh-before-send, and a "reconnect account" UX. |
| Quota and daily send caps | High | Gmail API enforces per-user rate limits and Workspace/free-tier daily send ceilings. Not a bulk sending platform. Must model peak volume against published limits. |
| Duplicate sends on retry | Medium | Gmail API has no idempotency key; a timeout-then-retry can double-send. Mitigate with per-message dedupe keys and conservative retry/backoff. |
| Dependency weight/conflicts | Medium | Pulls google/apiclient (+ service definitions) and its Guzzle/auth tree; can conflict with Laravel's pinned HTTP client versions. |
| Version lockstep | Medium | Bridge majors track Symfony Mailer majors; must match whatever Laravel pins (L10→Symfony 6.x, L11/12→Symfony 7.x). |
| Deliverability/observability gaps | Medium | No webhooks for bounces, complaints, or opens. Requires supplementary tooling or acceptance of reduced visibility. |
| Provenance/metadata anomaly | Low–Medium | "Repository: unknown" and a future-dated release (2026-05-29) in the supplied record. Verify on Packagist/GitHub before adoption; treat as a supply-chain hygiene gate. |
Key questions for engineering/product
Stack fit
Laravel 9+ only (Symfony Mailer era). PHP floor follows the bridge branch: 8.1 for the 6.4 line, 8.2 for the 7.x line. Verify against composer show symfony/mailer before requiring.
Compatibility matrix (validate at install time)
| Laravel | Symfony Mailer | Bridge line |
|---|---|---|
| 10.x | 6.2–6.4 | symfony/google-mailer:^6.4 |
| 11.x / 12.x | 7.x | symfony/google-mailer:^7.0 |
Pin the bridge to the same minor as symfony/mailer; do not let Composer resolve a bridge major that outruns the mailer.
Migration path (from SMTP or an ESP)
gmail as an additional mailer alongside the existing one; never replace in place.gmail behind a config flag.MailServiceProvider:Mail::extend('gmail', function (array $config) {
$client = new Google\Client();
$client->setClientId($config['client_id']);
$client->setClientSecret($config['client_secret']);
$client->setAccessToken($config['access_token']); // refreshed/persisted by us
$client->setScopes([Google\Service\Gmail::GMAIL_SEND]);
return new Symfony\Component\Mailer\Bridge\Google\Transport\GmailTransport($client);
});
gmail entry under config/mail.php mailers, driven by env vars.MAIL_MAILER and per-mailer routing as the rollback lever; a single env change reverts traffic.Sequencing
invalid_grant handling, account-reconnect UX, audit log.Testing note: GmailTransport cannot be captured locally — use log/array transports in tests and a dedicated Google test tenant for integration tests.
Maintenance
Low code churn (the bridge is a few hundred lines maintained by the Symfony core team, MIT). Real maintenance load is elsewhere: google/apiclient upgrades, Symfony major bumps riding on Laravel upgrades, and OAuth policy changes from Google. Budget one planned upgrade per Laravel major plus ad-hoc work when Google deprecates an auth flow. Run composer audit and pin the bridge exactly.
Support Failure tickets will cluster into four buckets, and support runbooks should mirror them:
429/rateLimitExceeded/dailyLimitExceeded → queue backoff plus a visible "delayed" state, not a hard failure.Without a bounce/complaint feed, support will absorb deliverability questions manually. Staff for that or add a supplementary provider.
Scaling
Failure modes to instrument
failed() handler.Ramp-up
How can I help you explore Laravel packages today?