Mailpit in WSL: Capture SMTP Messages Without Sending Test Mail
Run Mailpit inside WSL to inspect application email, verify SMTP payloads and links, and prevent local test messages from reaching real recipients.
Mailpit is a small email-testing service that accepts SMTP messages and provides a local web interface and API for inspection. It helps developers verify that applications construct expected subjects, recipients, HTML, text, headers, attachments, and links without sending routine test messages through a real mail provider. Running it in WSL is useful when the application itself runs in Linux, but the capture service must be treated as a local test dependency, not a mail relay or production SMTP server.
Mailpit’s official installation documentation lists port 1025 for SMTP and 8025 for the web UI by default. Its SMTP endpoint does not use encryption or authentication by default. Therefore, keep the listener on the intended local boundary, do not point a production application at it, and do not expose it on an untrusted network. A captured message can contain personal data, reset links, invoices, or one-time codes even when it is “only for testing.” Use synthetic recipients and data.
Install and start a single local capture service
Use the Mailpit installation page to select the current Linux binary or package for the WSL architecture. Record its version and checksum if using a release asset. Keep the executable, configuration, and message storage under a dedicated Linux path. Start the service in the foreground during initial setup so the logs and listener behavior are visible. Avoid running multiple instances against the same message database.
The default command starts the local service and listens on the documented SMTP and web ports. Verify the process and sockets rather than assuming defaults survived a custom configuration:
mailpit --version
mailpit
In a second WSL terminal, inspect the listeners and open the local web interface from the intended client. If another process already owns a port, choose a different one using Mailpit’s documented configuration options and update the application SMTP configuration to match. Do not terminate an unknown service just to reclaim a port.
For a systemd service, configure a dedicated Linux user, working directory, explicit storage behavior, and only the local interface needed. WSL services run only while the distribution is running; a systemd unit does not keep the Windows host or WSL VM awake. For developer sessions, a foreground process is often easier to reason about and clean up.
Point a development application at the capture endpoint
Set the application’s SMTP host and port to the WSL endpoint that it can actually reach. From an application process inside the same WSL distribution, 127.0.0.1 is often the simplest target if Mailpit is bound to loopback. If the application runs in Windows or another container, validate the route separately and use the address appropriate for that networking mode. Microsoft documents distinct NAT and mirrored networking behavior; do not assume a Linux loopback address is identical across processes.
Use a clearly synthetic sender and recipient such as [email protected]. Trigger one message for each path being tested: plain text, HTML, attachments, alternative recipient, and an error notification. Inspect the captured envelope recipients as well as the visible To header; applications can have different SMTP envelope and message-header values. Confirm that links render correctly and point to the intended test environment.
Keep the message generation deterministic. Use a known test object ID and a fixed locale/timezone so subject, date, and link assertions are reproducible. For HTML, inspect both the browser-rendered message and the raw source. A link that looks correct in a rendered page may have the wrong href, and a text-only fallback may be missing even when HTML is present.
Mailpit can expose a REST API for searching, retrieving, and deleting stored messages. Use the API to automate assertions in integration tests, but scope queries to a test run identifier or unique recipient. Delete only the messages created by that run. If authentication is configured for the UI/API, API requests must use the configured authentication; do not disable it broadly to simplify a test harness.
Guard against accidental delivery
The purpose of a capture server is to keep test mail out of real inboxes. Do not configure SMTP forwarding or message release unless the test specifically validates an approved relay path. Mailpit documents relaying and releasing messages as separate configuration and API features. Check those settings before importing a developer’s generic mail configuration into WSL.
Avoid using real recipient lists, live password reset URLs, customer names, or production database records in local test messages. Captured messages are stored state and may outlive the process depending on storage configuration. Apply a retention policy, restrict access to the UI/API, and clear test data on a schedule appropriate to the environment. Do not assume closing a browser clears captured mail.
When testing an SMTP client that requires authentication or TLS, configure Mailpit deliberately using its SMTP options and a test certificate if the protocol is under test. Mailpit’s default SMTP service is plaintext and unauthenticated; do not treat success against that default as proof that production TLS or authentication is configured correctly. Test production transport behavior against a controlled environment that matches the actual provider.
Validate mail behavior by layer
First verify that the application process can establish an SMTP connection to the expected host and port. Then inspect whether the message was accepted by Mailpit and whether the envelope recipient is correct. Next validate message content, encoding, links, and attachments. Finally, verify the application’s error path by temporarily using an unavailable test endpoint and confirming the user-facing retry or failure behavior. Do not induce failures against a live mail provider.
If no message appears, inspect application logs, SMTP host/port, WSL bind address, and whether the message was sent through a different configured backend. If the web UI cannot load from Windows, test it inside Linux first, then check WSL localhost forwarding and firewall rules. If duplicate messages appear, inspect application retries and whether the first SMTP acceptance succeeded before a timeout.
Automated tests should identify their messages with a unique run marker, such as a dedicated synthetic recipient or a non-sensitive custom header, then query only that run’s messages. Assert the SMTP envelope, MIME content type, required headers, and link destinations independently. If a test times out after the server accepted a message, retry logic may deliver a duplicate; make the assertion detect that case rather than silently choosing the newest message. Clean up through the documented API or a dedicated disposable store, and ensure parallel test workers do not erase each other’s messages.
Storage and cleanup
Mailpit stores captured messages in temporary or persistent SQLite-backed storage depending on configuration. Use the official storage guide for the chosen mode and release. A temporary store is convenient for ephemeral CI-style runs; persistent storage can retain developer messages across restarts. Do not copy a live storage database and call it a tested backup. Keep its path explicit and separate from application source.
For a disposable lab, stop Mailpit cleanly and remove only a known test data directory after confirming its resolved path. For automated tests, prefer unique runs and API-scoped deletion so one test does not erase another developer’s messages. If the WSL VHDX grows, identify whether the bytes belong to Mailpit storage, logs, packages, or application output before cleanup.
Include both text and HTML variants in test messages when the application supports multipart alternatives. Compare the visible body with the raw MIME parts, content type, character encoding, attachment filename, and disposition. Check that each test message uses a unique subject or header so automated API cleanup can select only that run. A message shown in the web interface is not sufficient to prove the raw structure matches a downstream client’s expectations.
Acceptance criteria
Accept the WSL Mailpit integration when the service version and ports are recorded, the listener is limited to the intended local access path, a test application sends to synthetic recipients, captured envelope and content assertions pass, and test messages cannot be released or forwarded accidentally. Confirm the chosen storage and cleanup policy.
Mailpit in WSL is a safe and efficient local email sink when kept isolated. It is not a production mail transfer agent, a proof of real-provider deliverability, or a substitute for testing authenticated TLS against the real delivery boundary.
Related:
- PHP and Composer in WSL: Align the CLI, Extensions, and Project Runtime
- Node.js in WSL: Keep npm, Native Modules, and the Workspace on Linux
Sources: