- Aug 11, 2026
- 5 min read
Mailpit vs Mailtrap vs listmonk: What Each One Is Actually For
When I sat down to add email to my lead pipeline (confirmations, unsubscribes, a few welcome sequences), three names kept coming up: Mailpit, Mailtrap and listmonk. They sound interchangeable. They aren’t. They sit at three different layers, and one of them can’t deliver a single email to a real inbox.
This is the decision as I recorded it, plus the reasoning I’d want to remember next time.
Three layers, not three alternatives
| Tool | Layer | What it does | What it doesn’t do |
|---|---|---|---|
| Mailpit | Development | An SMTP server that captures every message and shows it in a web UI | Deliver anything. Nothing leaves the box |
| Mailtrap (sending) | Delivery | Hands your message to real inboxes over an API or SMTP, with authentication, logs and bounce handling | Decide who should receive what, or when |
| listmonk | List management | Self-hosted newsletter manager: subscribers, lists, double opt-in, campaigns, templates | Deliver by itself: it needs an SMTP relay underneath |
The confusion comes from the fact that all three speak SMTP and all three show you emails in a browser.
Mailpit: a catcher, not a sender
I first thought of Mailpit as “the sender for development, and maybe production”. It’s a capture-only server: it accepts mail on port 1025, stores it, and shows it on 8025. That makes it perfect for:
- seeing exactly what a template renders like
- inspecting headers (that’s how I found a header bug that broke one-click unsubscribe)
- running the whole pipeline end to end without risking a real send
And useless as a production sender, by design. In my homelab
compose.yml it runs next to the lead hub, bound to 127.0.0.1 only, and I
reach its UI with an SSH port forward. One practical note: check the host
first. I already had a Mailpit on the default ports, and my first “test”
email silently landed in the old one.
Mailtrap: the delivery layer
Mailtrap has two products under one name: a testing sandbox (similar in spirit to Mailpit) and Email Sending, which delivers real mail. Sending has two streams, and the split matters:
| Stream | For | My use |
|---|---|---|
| Transactional | One-to-one messages someone triggered | The double opt-in confirmation, sent by the Cloudflare Worker seconds after a form submit |
| Bulk | Marketing to a list | Drip sequences, sent from the homelab |
Separating them keeps a bad marketing day from hurting the delivery of
confirmations. The Worker calls the transactional API with fetch; the
homelab’s sender adapter calls the bulk API. Both are behind interfaces, so
the provider is configuration, not code.
Why not listmonk
listmonk is genuinely good, and for a plain newsletter it would be my first recommendation. It gives you, out of the box, things I had to build: subscriber lists, double opt-in, unsubscribe pages, bounce processing, campaign scheduling.
It lost on three points for my case:
- Drips. listmonk is campaign-oriented: write a newsletter, send it to a list. Per-person sequences (“day 0, day 3, day 7 after you confirmed”) are not its strength.
- Another stateful service. It needs PostgreSQL, backups, upgrades and exposure for its public subscription and unsubscribe pages.
- Data in two places. My leads already land in Cloudflare KV from one forms Worker and are mirrored to SQLite on the homelab. With listmonk, every subscriber would have to be synced into its database and every unsubscribe synced back.
The DIY path reuses what already existed: the Worker handles double opt-in with signed links and unsubscribes at the edge, the homelab runs a small drip engine, and one suppression list covers both.
The cost of DIY: a checklist
Choosing DIY means owning the compliance features listmonk would have provided. I wrote them down as a definition of done before any production send:
- confirmation email within seconds of subscribing
- confirm and unsubscribe endpoints with signed tokens, tested
- one-click unsubscribe plus a visible link in every marketing email
- suppression honoured by every sender path, tested
- bounce and complaint webhooks feeding the suppression list
- SPF, DKIM and DMARC on every sending domain
- privacy pages naming the email provider and describing double opt-in
If you can’t commit to that list, use listmonk (or a hosted newsletter tool) and let it carry those obligations.
Which domain sends
My root domains have mailboxes on a privacy-focused provider that I use for
writing to people by hand. I didn’t want automated mail touching their
reputation or their DMARC policy, so automated email goes out from a
dedicated sending subdomain (planned: news.jalcocertech.com), shared by
all my sites:
- From:
noreply@on the sending subdomain, with a per-brand display name (“FOSS Engineer”, “JAlcocerTech”, …). - Reply-To: the brand’s real mailbox, so a reply reaches a human.
While testing I used a verified test subdomain I already had in Mailtrap; moving to production is a change of one setting once the new subdomain is verified.
SPF, DKIM, DMARC in one paragraph each
- SPF lists which servers may send for the domain. The provider tells you
which
include:to add to the subdomain’s TXT record. - DKIM signs each message with a key published in DNS, so receivers can verify it wasn’t altered and really came from the domain.
- DMARC tells receivers what to do when SPF/DKIM don’t align with the
From:domain (none,quarantine,reject) and where to send reports.
Gmail and Yahoo require all three, plus one-click unsubscribe, for bulk senders. Putting them on a subdomain keeps experiments away from the root domain’s mail.
Takeaways
- Mailpit is for seeing mail, Mailtrap is for delivering it, listmonk is for managing lists. Don’t compare them as alternatives.
- Use transactional and bulk streams separately.
- listmonk is the right answer for campaign newsletters; DIY made sense here because drips and an existing lead store were the core.
- Send from a dedicated subdomain, reply to a real mailbox, authenticate with SPF, DKIM and DMARC.
Next: a pull-only homelab mirroring Cloudflare KV to SQLite without a tunnel.