Guide
How to use Mailpit with PHP.
Every PHP app sends email: password resets, order confirmations, contact forms. Locally you want to see those messages without any of them reaching a real inbox. Mailpit catches them all. This guide sets it up with plain PHP, Laravel, WordPress and Symfony, then shows how Bothy, my Mac app for local PHP development, wires it up for every site.
Install Mailpit (brew install mailpit), which listens for SMTP on port 1025 and shows a web inbox on port 8025. Point PHP's mail() at it with sendmail_path, and point frameworks at 127.0.0.1:1025 with no auth or encryption.
What Mailpit is
Mailpit is a small, open-source mail catcher: an SMTP server that accepts everything and delivers nothing, plus a web interface to read what arrived. It's a single Go binary and is widely used as the maintained successor to MailHog.
- SMTP on port 1025 accepts mail from your app, with no authentication by default.
- Web UI on port 8025 shows each message's HTML, plain text, headers, raw source and attachments, with search.
- It has tools for checking HTML compatibility across mail clients and finding broken links, plus a REST API that automated tests can query.
The point is safety as much as convenience. A local copy of a production database is full of real customer addresses. Without a catcher, one test checkout can email a real person.
Install and run Mailpit on macOS
- 1Install with Homebrew
brew install mailpit
- 2Start it as a background service
It starts again at login. To try it once in the foreground instead, run
mailpitin a terminal.brew services start mailpit
- 3Open the inbox
The web UI is at
http://localhost:8025. SMTP is listening on port 1025.open http://localhost:8025
Plain PHP: route mail() through Mailpit
PHP's mail() doesn't speak SMTP on macOS. It pipes the message to the program in sendmail_path, which defaults to the system's /usr/sbin/sendmail. Mailpit ships a sendmail-compatible command that forwards to its own SMTP server.
- 1Set sendmail_path in php.ini
Find the file with
php --ini. Use/usr/local/bin/mailpiton Intel Macs. If php-fpm uses a different ini file from the CLI, set it in both.; php.ini sendmail_path = "/opt/homebrew/bin/mailpit sendmail"
- 2Restart php-fpm
The web server's PHP only reads php.ini at startup.
brew services restart php
- 3Send a test message
It should appear at
localhost:8025straight away.php -r 'var_dump(mail("test@example.com", "Hello from PHP", "It works."));'
Laravel
Laravel sends mail over SMTP with Symfony Mailer. Set these in .env. Leave the username, password and encryption empty, because Mailpit doesn't use them.
MAIL_MAILER=smtp
MAIL_HOST=127.0.0.1
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_FROM_ADDRESS="hello@example.test"
MAIL_FROM_NAME="${APP_NAME}"If you've cached config, run php artisan config:clear afterwards. Mailables that implement ShouldQueue only send when a queue worker is running.
WordPress
wp_mail() uses PHPMailer, which by default calls PHP's mail(). If you've set sendmail_path as above, WordPress mail already goes to Mailpit and there's nothing else to do.
To use SMTP instead, for example because production uses an SMTP plugin and you want the same code path, drop a must-use plugin into wp-content/mu-plugins/mailpit.php:
<?php
add_action( 'phpmailer_init', function ( $phpmailer ) {
$phpmailer->isSMTP();
$phpmailer->Host = '127.0.0.1';
$phpmailer->Port = 1025;
$phpmailer->SMTPAuth = false;
$phpmailer->SMTPAutoTLS = false;
} );
A quick test with WP-CLI: wp eval 'wp_mail("test@example.com", "Test", "Hello");'. If an SMTP plugin from production is active locally, it may override this hook with real credentials. Deactivate it on your local copy.
Symfony
Symfony Mailer takes a single DSN. Put it in .env.local so it never reaches production config.
MAILER_DSN=smtp://127.0.0.1:1025 # then php bin/console mailer:test test@example.com
Symfony Messenger may queue mail asynchronously, in which case it only sends once a consumer runs.
Pitfalls and troubleshooting
mail()returns true but nothing arrives. The web server's PHP is still using the oldsendmail_path. Compare the value inphpinfo()with the CLI, and restart php-fpm. A true return only means the message was handed to the sendmail command.Connection refusedon port 1025. Mailpit isn't running (brew services list), or something else is on that port (lsof -i :1025).- TLS or STARTTLS errors. The client is trying to encrypt a connection Mailpit doesn't offer. Set encryption to none. In PHPMailer that's
SMTPAutoTLS = false, and in a Laravel.envleave the encryption setting empty. - Messages disappear after a restart. By default Mailpit keeps its messages in a temporary database. Start it with
--database /path/to/mailpit.dbif you want them kept. - A real email went out. The app sent through an API-based mailer (Mailgun, Postmark, SES) instead of SMTP or
mail(). Switch the local mailer to SMTP, or use the provider's sandbox mode.
Mailpit with Bothy
Bothy ships Mailpit as an add-on. Enable it once and PHP's mail() is wired to it automatically for every site. There's no sendmail_path to edit. The inbox is at https://mail.test, and unread messages show as a badge on Mail in the sidebar. Frameworks that use SMTP get the same settings as above: host 127.0.0.1, port 1025, no auth.
bothy mail --enable # install and start Mailpit
bothy mail # open https://mail.test
bothy mail --list # recent messages
bothy mail --clear # empty the inbox
The Laravel, Symfony and WordPress starters create a site with its database config already written. For Laravel and Symfony, set the mail variables as above, which you can do from the site's Environment tab or with bothy site env. Mailpit is also reachable through the MCP server, so an AI agent can trigger a password reset and then read the email it produced.
Questions
SMTP on 1025 and the web interface on 8025 by default. Both can be changed with the --smtp and --listen options.
Yes, for most people. It does the same job with the same default ports, is actively maintained, and adds search, HTML checks and an API.
Not by default. It captures everything. It has an optional relay feature for releasing selected messages, but you have to configure that explicitly.
Set sendmail_path = "/opt/homebrew/bin/mailpit sendmail" in php.ini and restart php-fpm.
No. Locally, leave username, password and encryption empty. Some clients need TLS turned off explicitly.
Doing it with Bothy: bothy mail --enable, and every site's mail() goes to Mailpit, readable at https://mail.test. See Mailpit in Bothy.