binarybuilds/laravel-mail-manager
Laravel Mail Manager adds a web dashboard for creating, editing, previewing, and managing email templates and mailables in Laravel apps, with handy tools for sending test messages and reviewing mail activity.
Install the package and publish its assets:
composer require binarybuilds/laravel-mail-manager
php artisan vendor:publish --provider="BinaryBuilds\MailManager\MailManagerServiceProvider"
php artisan migrate
If the provider isn’t auto-discovered, add it to config/app.php. Use the publish tags shown by php artisan vendor:publish if the provider publish isn’t available.
Open config/mail-manager.php first. Review:
Protect the dashboard before using it anywhere real. Put it behind auth plus an admin/ops gate, or restrict it to non-production environments until you’ve reviewed what it stores.
First use case: send a test mailable through a deliberately broken or sandbox SMTP configuration, confirm the failure is captured, open the mail-manager dashboard, inspect the error and payload, fix the transport, then resend from the UI.
The package is best treated as an operational tool: a lightweight failed-mail inbox and replay button for support and ops, not a full email delivery platform.
Keep sending mail normally. Continue using Laravel’s Mail facade and Mailable classes. The package is intended to observe/capture failures around standard Laravel mail sending, so wrapping every send in package-specific code usually adds unnecessary coupling.
Use queued mail for production paths. Queued mailables plus a real queue worker give you retries and failed-job visibility. Combine the package dashboard with Laravel’s failed_jobs table so you can distinguish “mail job failed before send” from “mail transport rejected the message.”
Use it as a support workflow. Search by recipient, subject, and date; inspect the stored error; confirm the underlying issue is fixed; resend. This is especially useful for password resets, invoices, order confirmations, and other transactional mail where a support person needs a safe replay action without SSH access.
Gate resend capability separately from view capability. Viewing failed mail may be acceptable for support; resending should usually be limited to senior support/ops. If the package only exposes one middleware stack, enforce the distinction in your route/gate layer or wrap the dashboard route.
Add environment controls. Enable capture in staging and production, but consider disabling it locally where MAIL_MAILER=log produces noise. If the config supports sampling or recipient allowlists, use them to avoid storing every marketing/system email.
Prune aggressively. Failed mail records can contain full HTML bodies, headers, recipient data, and sometimes reset links or tokens. Schedule pruning with the package’s retention config or a small custom command. Treat the table as sensitive operational data.
Use it alongside, not instead of, failed-job tooling. Horizon, queue:failed, and Sentry/Flare still matter. Mail-manager answers “what was the email and can I resend it?”; your queue tooling answers “why did the worker die?”
Wrap programmatic replays if needed. If the package exposes a service/facade for resending, call it from an artisan command or internal admin action rather than duplicating transport logic. If not, keep a small internal command that reads the stored record and reconstructs a mailable.
Age is the biggest risk. Last release was October 2020, which predates Laravel 9’s switch from SwiftMailer to Symfony Mailer. Expect compatibility work on modern Laravel versions, especially around message bodies, headers, and attachments. Pin Laravel/PHP versions or fork and patch before relying on it in production.
Test the full failure path, not just the dashboard. Confirm that a real SMTP rejection or queued-mail exception actually creates a record. Some failures surface only in failed_jobs; others may be captured by the package. Check both during rollout.
Resending can duplicate mail. A message may have been accepted by the remote server but failed later in the pipeline. Before replaying, check recipient complaints and the original error. For critical mail, add an idempotency key or a manual “already resent” note in your process.
Attachments and inline images are fragile. Stored attachment paths may be temporary, deleted, or inaccessible from the web/queue context. Test resend with attachments explicitly; don’t assume a stored record is fully replayable.
PII and secrets live in the database. Email bodies may contain password-reset tokens, invoice links, or personal data. Restrict database access, redact where possible, and prune on a schedule. Never expose the dashboard publicly, even briefly.
Clear config cache after publishing. Run php artisan config:clear and php artisan route:clear after changing middleware, route prefix, or retention settings. A cached config is a common reason dashboard changes appear not to work.
Watch for route and migration collisions. Review the published migration name and dashboard route prefix. If you already have a /mail or /mail-manager route, adjust the package prefix in config before deploying.
HTML rendering should be treated as untrusted. If the dashboard renders stored email HTML directly, sanitize it or render in a sandboxed iframe. A compromised or unexpected email body should not be able to run scripts in your admin session.
Mail::fake() won’t exercise the package. Feature tests using Mail::fake() bypass real sending and therefore won’t validate capture/resend behavior. Add a small integration test against a sandbox transport or a fake transport that throws.
Pruning is not optional. Logging all outgoing mail can grow the table quickly. Set retention based on support needs—often 7–30 days for failed records—and monitor table size.
Have an exit plan. Because the package is small and old, isolate its usage behind your own admin route or internal service where practical. That makes it easier to replace with a custom failed-mail table, Horizon dashboard, or a maintained fork if the package stops working on future Laravel releases.
How can I help you explore Laravel packages today?