All posts
Open-source SMTP servers and MTAs for Linux: choose the layer
Gabe 12 min read

Open-source SMTP servers and MTAs for Linux: choose the layer

For Linux developers choosing email infrastructure, Postfix, Exim and OpenSMTPD handle transfer and queues; Haraka exposes SMTP plugins; Postal adds application APIs. Compare their responsibilities, direct delivery versus smarthosts, and MailKite Server's narrower backend-led model before choosing what to operate.

Submission, transfer and mailbox access are separate jobs An app or email client submits authenticated mail on port 587 or 465. A transfer service owns the queue and retries, then sends to the destination MX on port 25. The recipient's mailbox service separately exposes stored mail through IMAP on port 993. Follow the message,not the product label 1 · Authenticated submission App or client → submission587 STARTTLS / 465 TLS 2 · Transfer and queue Persist accepted messagesChoose route; retry failuresLocal MTA or upstream relay 3 · Destination MX SMTP transfer on port 25Recipient accepts or rejects 4 · Recipient mailbox Storage + mailbox serviceIMAP 993 → reader's clientIMAP reads stored mail.It doesn't submit this message.
One deployment can implement several roles. The boundaries still determine authentication, persistence and failure handling.

SMTP is the protocol; an MTA owns transfer

Choose the responsibility before the daemon. SMTP carries messages between systems. A message transfer agent accepts responsibility for mail, selects a delivery route, and handles unsuccessful attempts. A submission service accepts mail from authorized users or applications. The same software can perform submission and transfer with different listeners and policies.

An internet-facing MX normally receives server-to-server SMTP on port 25. Client submission normally uses authentication with STARTTLS on 587 or implicit TLS on 465. Requiring employee credentials on the public MX would stop ordinary remote servers delivering mail. Allowing anonymous external-to-external relay would create the opposite problem: anyone could send through you.

IMAP has a separate job: exposing stored messages, flags and folders to readers. Postfix plus a mailbox service creates different operating responsibilities from an SMTP relay alone. The Linux email-server overview covers complete stacks; the IMAP comparison focuses on mailbox behavior.

The useful question isn’t whether a project “supports SMTP.” It’s where the message goes after acceptance, and who can explain its eventual outcome.

Compare queue ownership, policy and application interfaces

These projects overlap at SMTP, but they don’t deliver the same finished system. This matrix describes documented architecture, not a throughput ranking. None of the traditional MTAs below supplies a full employee mailbox experience merely by listening for SMTP.

ProjectQueue and delivery modelPolicy or application surfaceWhat you still operate
PostfixQueue manager and delivery agents; direct delivery or relayAccess rules, policy services, Milters, transportsFilters, credentials, DNS, monitoring; separate IMAP
EximSpool, retries, routers and transportsACLs and configuration expansionsRouting policy, filters, monitoring; separate IMAP
OpenSMTPDQueued envelopes dispatched through actionsMatch/action rules, tables and filtersLinux portable package, integrations; separate IMAP
HarakaDisk-backed outbound queue; queue plugins can change handoffJavaScript SMTP and outbound hooksPlugin policy and any application pipeline
PostalApplication delivery platform with queues and delivery recordsHTTP send API, SMTP, inbound routing and webhooksPlatform services, DNS and sending infrastructure
MailKite ServerBackend-owned storage; external send through configured smarthostHaraka edges, routes and core send/read APIReference backend, edges, storage and upstream relay

Compare the persistence boundary as carefully as the interface. A queue plugin that hands mail to your HTTP endpoint isn’t automatically a retrying event system. Conversely, a delivery log in a web UI doesn’t imply that users can synchronize folders through IMAP.

Keep Postfix or Exim when the missing piece is narrow

A working MTA is often the right base for one more integration. Postfix’s architecture overview separates reception, cleanup, incoming/active/deferred queues and delivery agents. That gives you distinct places to enforce acceptance policy, inspect content, select a transport, or deliver into a mailbox service through LMTP.

Suppose a service must reject unknown project addresses. A recipient lookup may solve that without replacing queue management. If accepted mail needs a ticket importer, a delivery transport can hand it to that program. Rejecting before acceptance leaves responsibility with the sender. After acceptance, your MTA retries temporary failures and reports permanent or exhausted failures to the sender (RFC 5321, §4.2.5).

Exim offers flexible routing and incoming-mail checks. Its ACLs decide whether mail is accepted; routers choose where it goes, and transports perform delivery. This is attractive when your team already understands the configuration and needs conditional routing rather than a new HTTP product.

Neither choice removes application work. You’ll still define how identifiers cross into your app, how failures are reported, and how duplicate deliveries are handled. For hook and transport details, use the programmatic Linux email-server guide. A replacement is justified when the existing boundary is wrong, not just because another project has a newer dashboard.

OpenSMTPD and Haraka offer different kinds of extension

OpenSMTPD emphasizes declarative mail routing; Haraka puts JavaScript at SMTP boundaries. OpenSMTPD originates in OpenBSD and provides a portable implementation for other systems, including Linux. Its ISC license and relatively focused role make it worth considering when you want an MTA rather than an application platform.

The official smtpd.conf manual describes matching envelopes to actions, saving them in the queue, and dispatching delivery later. Actions include relay, LMTP and local delivery; filters can make decisions during SMTP phases. Check the manual shipped with your Linux package before translating current OpenBSD examples: portable releases and distribution packages need not share every new directive.

Haraka’s outbound documentation explicitly describes mail queued on disk, destination MX lookup, temporary-failure retries and bounce hooks. It’s incorrect to describe Haraka as only an inbound parser or to claim all outbound retry machinery must be written from scratch.

The distinction is what you’re extending. JavaScript recipient validation and custom next-hop selection fit Haraka. Reliable delivery of parsed events to your application adds another boundary: durable storage, acknowledgements, retries and visibility when the endpoint is unavailable. A hook gives control; it doesn’t prove that the resulting application pipeline is complete. Choose OpenSMTPD for its routing model, Haraka for plugin-level control, and evaluate your integrations rather than inferring security from programming language or project ancestry.

Postal fits application delivery rather than employee mailboxes

Postal is the closer comparison when you need a self-hosted application-email platform. Its official feature list documents SMTP and HTTP sending, queue visibility, DKIM signing, delivery webhooks, multiple credentials and inbound forwarding to HTTP or SMTP destinations.

Those features change the integration job. An app can use a documented send interface and consume delivery events instead of building a reporting layer around an MTA spool. Operators can investigate message history in Postal’s web interface. That interface is for delivery administration, not a substitute for users’ Drafts, Trash, calendar or mailbox synchronization.

Choose Postal when those application surfaces are the requirement and you’re prepared to operate its platform. Keep Postfix or Exim when existing routing and filters already fit. Choose Haraka when the SMTP conversation itself needs custom code. Feature breadth isn’t an operational-cost measurement: databases, upgrades, queue growth, backups and incident response all depend on your deployment and traffic.

Direct delivery and smarthost delivery move responsibility

A smarthost changes the next hop; it doesn’t remove your submission policy or application failures. Direct delivery means your sender resolves the recipient’s MX and connects to it. With a smarthost, your system hands mail to a configured relay, which performs the onward delivery.

Two outbound paths and their operational owners Direct delivery goes from your queue to recipient MX servers and leaves egress, source-IP identity and retry policy with you. Smarthost delivery goes from your service to an authenticated relay and then to recipient MX servers. The relay operates onward transfer; you still handle credentials, domain authorization and failed handoffs. Who owns the next hop? Direct-to-MX Your MTA + queue Recipient MX · port 25 You: egress, PTR, source IP,retries and failure notices Authenticated smarthost Your service Upstream relay + queue Recipient MX · port 25 You: authorized sender, secrets,handoff errors and app retries
Generic transport choices, not a claim that every project implements both paths or identical retry behavior.

For direct delivery, check outbound port 25 availability before choosing a host. PTR records belong to the IP’s reverse-DNS operator, usually your hosting provider; owning the forward DNS zone isn’t enough. Inbound port 25 reachability is a separate check if this host will also receive internet mail.

Publish SPF for the actual sending path, sign with DKIM where appropriate, and configure DMARC alignment for the visible From domain. TLS protects a connection, SMTP AUTH authorizes submission, and domain authentication provides other signals. None guarantees inbox placement. An upstream relay still needs permission to send for your domain.

Monitor the oldest queued message and failure reasons, not only the number of messages. Distinguish temporary deferrals, permanent rejections, relay-authentication errors and exhausted retries. “Accepted by our server” and “accepted by the destination” are different events; neither means the recipient read it.

MailKite’s backend model is useful within a narrower scope

An inbound application workflow needs storage and event delivery after SMTP acceptance. The DIY shape is an SMTP edge validating recipients, a durable backend storing MIME, and workers delivering events to your application. Submission requires its own authorization and an explicit outbound transport. That’s a useful design even if you assemble it with Haraka and your own database.

MailKite Server, which we build, packages that backend-led approach. Its public README describes Haraka MX and submission edges, a SQLite reference backend, signed webhook retries and a web console. It’s AGPL-3.0-only and pre-1.0. The console is operational tooling, and the thin IMAP head isn’t a full employee mailbox server.

The developer API contract makes the outbound limit concrete: external sending requires a configured smarthost. Without one, 400 no_smarthost refuses the send before storage. A relay failure returns 502 after the Sent copy is stored. Neither that copy nor this error contract provides a durable automatic outbound retry guarantee; investigate an ambiguous handoff before resending to avoid duplicates.

Handoff exercise: a MailKite Server send returns 502, and the message is visible in Sent. Your application proposes an immediate resend because the request failed. Does Sent prove delivery, does the error prove non-delivery, and what evidence would change your retry decision?

Reveal what a Sent copy and relay error actually establish

The copy establishes local storage, not relay acceptance or destination delivery. The 502 establishes a failed relay attempt from the API's perspective, but the response alone doesn't distinguish an explicit rejection from a lost response after the relay accepted the message. It also doesn't promise that this reference backend has queued an automatic retry.

Correlate the attempt with backend and relay records before resending. An explicit pre-acceptance rejection supports correcting that failure and retrying; confirmed relay acceptance means follow the relay's onward delivery record instead of submitting another copy. If acceptance is still ambiguous, preserve that uncertainty and use your application's reconciliation policy. A permanent recipient rejection needs a changed address or other correction, not the same blind retry. This exercise concerns the documented Server contract, not a guarantee about MailKite Cloud or other MTAs.

Core send/read JSON compatibility isn’t full hosted API parity. Templates, scheduling and structured attachments are refused locally, and tracking flags are no-ops.

The separate commercial MailKite Cloud product offers a managed SMTP relay. Consider Server for backend-owned application mail and Cloud when you want a managed upstream. Postal is an alternative for a self-hosted API delivery platform; Postfix, Exim or OpenSMTPD are better fits for conventional MTA operations. The open-source page describes the split. For human mailbox requirements, continue with the client and webmail comparison rather than treating any SMTP endpoint as a complete inbox.

Primary sources and maintenance

Sources retrieved 2026-10-14. Refresh features, release status and license evidence by 2026-11-13. These are documentation-based comparisons; no delivery benchmarks or interoperability results are claimed.

Discuss this post: Hacker News Share on X Share on LinkedIn

Related posts