Haraka review: a Node.js SMTP server built around plugins
A fast, highly extensible, and event driven SMTP server
At a glance
- What is it?
- Haraka is an event-driven SMTP server written in JavaScript, deployed as a filtering MTA or submission agent rather than a mail store. Its plugin hooks make unusual mail routing a small file, but it assumes you already run the systems that store and serve mail.
- Who is it for?
- Adopt Haraka when you need an SMTP front end whose behaviour you can rewrite in a few lines of JavaScript, and you already run a separate store, LDA, or IMAP backend. Do not adopt it as a mailbox server: the README states plainly that it is not a mail store, an LDA, or an IMAP server.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Haraka is for, and what it refuses to be
Haraka solves the problem of mail that arrives and needs decisions applied to it before anyone reads it. It is an SMTP server: it speaks the protocol, accepts connections, and runs your logic at each stage of a transaction. The README describes it as a filtering MTA and as an MSA on port 465 with the legacy 587 also supported, with the auth and DKIM plugins enabled. That framing matters. Haraka sits in front of, or beside, the systems that actually hold mail.
The README is explicit that Haraka is not a mail store, not an LDA, and not an IMAP server, and that it is designed to work alongside those systems. If you want a single binary that receives mail and lets users read it over IMAP, this is the wrong project and no plugin will change that. The audience is narrower than the repository's topic list suggests: operators who need spam filtering, address rewriting, or authenticated submission, and who already have a delivery target. A second group is developers who need to accept mail programmatically and would otherwise write their own SMTP parser. The built-in outbound engine covers the other direction: the README states that mail flagged as relaying, for example by an auth plugin, is queued for outbound delivery automatically.
Hooks, plugins, and where a message actually goes
The mechanism is a fixed sequence of hooks per SMTP transaction: connect, helo, mail, rcpt, data, data_post, queue, and more. Each hook is a place where a plugin can attach a function. Plugins are asynchronous by default, so a lookup against DNS, Redis, or an HTTP API does not block the server while it is in flight. That is the whole architecture in one sentence, and it explains why the project can describe itself as handling thousands of concurrent connections: the work of a transaction is spread across callbacks rather than a thread per connection.
The README gives a concrete example of how small a behaviour change can be. Accepting qmail-style tagged addresses and rewriting [email protected] to [email protected] before forwarding to an Exchange or IMAP backend is, in the README's words, roughly this:
exports.hook_rcpt = (next, connection, params) => {
const rcpt = params[0]
const [user] = rcpt.user.split('-')
rcpt.user = user
next()
}Two things are worth noticing. First, the hook mutates the recipient object and calls next() to continue the chain. Second, the plugin file lives in the service directory's plugins/ folder, which means custom logic is deployed the same way as anything from the registry. The registry itself, Plugins.md, covers auth, DNSBLs, DKIM, SpamAssassin, rspamd, Redis, ClamAV, and queue backends. The queue hook is the one that hands a message to a backend, so replacing the default forwarding behaviour means writing a plugin at that point rather than editing server internals.
Installing Haraka and sending a first message through it
Haraka requires Node.js. The README's install path is a global npm package, then a service directory created by the CLI. The package.json engines field pins Node.js at >=20, so check your runtime before anything else.
npm install -g Haraka
haraka -i /path/to/haraka_testThe second command creates haraka_test with config/ and plugins/ subdirectories and sets the host name from hostname(1). Nothing is listening yet. Before starting the server you must edit config/host_list and add the domains for which Haraka should accept mail. If that file does not list a domain, mail for it is not accepted.
haraka -c /path/to/haraka_testStarting with -c points the server at that directory. By default, per the README, mail addressed to domains in config/host_list is accepted and forwarded through the smtp-forward plugin, which reads config/smtp_forward.ini. To change which plugins run, edit config/plugins. Per-plugin documentation is available from the CLI:
haraka -h plugins/<name>If you prefer to run from a checkout rather than the global package, the README gives that path too:
npm install
node haraka.jsThe repository also carries a Dockerfile, but its own header states it is user-contributed and not officially supported by the Haraka team. It builds on phusion/baseimage:focal-1.2.0, installs Node.js 18 from the NodeSource script, copies config/host_list and config/plugins into /usr/local/haraka, and exposes port 25. The mismatch between that Node.js 18 and the engines field of >=20 is visible in the two files, and anyone using the container should resolve it deliberately rather than assume the image matches the release.
The operational cost of a plugin-first server
Flexibility has a price, and Haraka's price is that the server does very little you did not configure. A fresh service directory forwards mail through smtp-forward; everything else, from DNS blocklists to DKIM signing to greylisting, is an optional dependency listed in package.json and an entry in config/plugins. The optionalDependencies list is long, and each plugin brings its own configuration file and its own failure modes. A plugin that performs a network lookup can time out; a plugin that writes to Redis needs Redis reachable. Because hooks are asynchronous, a slow plugin does not block the event loop in the way a synchronous call would, but it does extend the lifetime of the transaction, and the connection stays open while it waits.
The second cost is that Haraka is not a mail store, so anything you accept must go somewhere. The README directs you to configure smtp-forward, or to write a queue plugin for another backend. If your backend is down, the responsibility for what happens to the message is in your configuration, not in Haraka's defaults. This is the case where Haraka is the wrong tool: a small site that wants one daemon to receive, store, and serve mail has to assemble at least three components, and the README does not document rollback or a built-in spool that survives an unavailable destination. The documentation set (docs/, Plugins.md, the tutorial) is where that ground would be covered; the README itself stops at describing the forwarding default.
Haraka compared with Postfix
Postfix is the obvious alternative and the difference is not performance claims, it is where the logic lives. Postfix is configured through text parameters, lookup tables, and a set of daemons with fixed roles; changing behaviour generally means combining existing directives or writing a policy daemon that speaks a defined protocol back to the MTA. Haraka inverts that. The SMTP transaction is exposed as JavaScript hooks, and a behaviour that would need a policy daemon in Postfix is a function in a plugin file, as the tagged-address example shows.
That inversion cuts both ways. In Haraka, a bug in your plugin runs inside the mail server process, and the set of behaviours you get out of the box is smaller. In Postfix, the configuration language constrains what you can express, but the components are mature and the operational literature is large. The choice is roughly: do you want to express mail policy as code, or as configuration over a fixed set of daemons? Haraka is also a Node.js program, so its dependency surface is the npm ecosystem, which is a different kind of maintenance commitment than a distribution-packaged MTA.
Maintenance, releases, and the MIT licence
The repository is not archived, and the last push was on 2026-09-05, the same day as the v3.3.4 release. The two releases before that were v3.3.3 on 2026-08-04 and v3.3.2 on 2026-07-21, so the recent cadence is roughly monthly. The current version in package.json is 3.3.4. The README names Matt Simerson as the current maintainer and credits Matt Sergeant as the creator, with CONTRIBUTORS.md holding the full list. SECURITY.md exists in the repository for the security policy and reporting process, and CHANGELOG.md carries release notes, so an upgrade path is documented rather than implied.
Upgrade cost concentrates in the optional plugin packages. The core dependencies are versioned with tilde ranges in package.json, which permits patch updates within a minor line, and the optional plugins are listed the same way. A major version bump in a plugin is where configuration keys can move, and the per-plugin documentation retrieved with haraka -h plugins/<name> reflects the installed version, so it is the check to run after upgrading rather than before. Haraka is MIT licensed, which permits commercial and closed-source use and modification, with the usual requirement that the licence text and copyright notice travel with redistributed copies. That is a description of the licence terms, not advice about your situation.
Editorial conclusion
Adopt Haraka when you need an SMTP front end whose behaviour you can rewrite in a few lines of JavaScript, and you already run a separate store, LDA, or IMAP backend. Do not adopt it as a mailbox server: the README states plainly that it is not a mail store, an LDA, or an IMAP server. Before committing, verify that your Node.js runtime satisfies the engines field (>=20), that config/host_list contains exactly the domains you intend to accept mail for, and that config/plugins lists only the plugins whose configuration files you have actually written.
Frequently asked questions
What is Haraka?
Haraka is a Node.js SMTP server with a plugin architecture, described in its README as a highly scalable SMTP server that is widely deployed as a filtering MTA or as an MSA. It is not a mail store, an LDA, or an IMAP server, and is designed to work alongside those systems.
How does Haraka compare with Postfix?
The difference is where mail policy lives. Postfix is configured through parameters, lookup tables, and a fixed set of daemons, while Haraka exposes each SMTP transaction as JavaScript hooks such as connect, helo, mail, rcpt, data, and queue, so a behaviour change is a plugin file.
What are the alternatives to Haraka?
The README positions Haraka against mail stores, LDAs, and IMAP servers by stating it is none of those and works alongside them. Among SMTP servers, Postfix is the comparison the README's own framing invites, since Haraka replaces configuration-over-daemons with plugin hooks in JavaScript.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/haraka-haraka)