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.

In short

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

  1. 1
    Install with Homebrew
    brew install mailpit
  2. 2
    Start it as a background service

    It starts again at login. To try it once in the foreground instead, run mailpit in a terminal.

    brew services start mailpit
  3. 3
    Open 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.

  1. 1
    Set sendmail_path in php.ini

    Find the file with php --ini. Use /usr/local/bin/mailpit on 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"
  2. 2
    Restart php-fpm

    The web server's PHP only reads php.ini at startup.

    brew services restart php
  3. 3
    Send a test message

    It should appear at localhost:8025 straight 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 old sendmail_path. Compare the value in phpinfo() with the CLI, and restart php-fpm. A true return only means the message was handed to the sendmail command.
  • Connection refused on 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 .env leave 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.db if 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

What ports does Mailpit use?

SMTP on 1025 and the web interface on 8025 by default. Both can be changed with the --smtp and --listen options.

Is Mailpit a replacement for MailHog?

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.

Can Mailpit send mail to real addresses?

Not by default. It captures everything. It has an optional relay feature for releasing selected messages, but you have to configure that explicitly.

How do I make PHP mail() go to Mailpit?

Set sendmail_path = "/opt/homebrew/bin/mailpit sendmail" in php.ini and restart php-fpm.

Do I need authentication or TLS for Mailpit?

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.

Get Bothy for $69.
One payment for every 1.x release. Version 2 will be a separate purchase.
Buy now