Haraka SMTP Server
Haraka is a highly scalable Node.js email server designed for modularity and high performance. It features a robust plugin architecture, enabling developers to easily extend its functionality for tasks such as spam protection, custom routing, and authentication. Haraka excels as a filtering Message Transfer Agent (MTA) or a Mail Submission Agent (MSA) on port 587 with authentication, complementing traditional mail systems like Postfix or Exchange without attempting to be a mail store or IMAP server. The current stable version is 3.1.5, with frequent releases addressing bug fixes, dependency updates, and minor enhancements. Its key differentiators include an asynchronous, event-driven design for speed, excellent built-in spam protection capabilities through plugins, and an easily extensible system for handling complex mail flow scenarios with minimal code.
Common errors
-
Error: listen EACCES: permission denied 0.0.0.0:25
cause Haraka is attempting to listen on a privileged port (like 25) without sufficient user permissions (e.g., not run as root).fixRun Haraka with `sudo`: `sudo haraka -c /path/to/haraka_instance`. -
Error: listen EADDRINUSE: address already in use :::25
cause Another process (e.g., Postfix, Sendmail, another Haraka instance) is already listening on the configured SMTP port.fixStop the conflicting process, or configure Haraka to listen on a different port by editing `config/smtp.ini`'s `listen` setting. -
Error: Cannot find module 'Haraka' or Error: Cannot find module 'haraka'
cause The `haraka` package is not installed or not discoverable in the current Node.js environment/path.fixIf installed globally, ensure your `PATH` includes npm's global bin directory. If installing locally for development, ensure you run `npm install` in the project root. For CLI usage, `npm install -g Haraka` is usually sufficient. -
550 5.7.1 Relaying denied (or similar 'Mailbox unavailable' error for valid recipient)
cause Haraka is configured to accept mail only for domains listed in `config/host_list`, and the recipient's domain is not present in that file.fixAdd the recipient's domain (e.g., `example.com`) to the `config/host_list` file in your Haraka instance directory and restart Haraka.
Warnings
- breaking Haraka v3.1.0 introduced a significant breaking change by consolidating several disparate configuration files (e.g., `haproxy_hosts`, `smtpgreeting`, `databytes`) into a single `connection.ini` file. Existing installations on older versions will need to migrate their settings.
- breaking As of Haraka v3.1.2, the minimum required Node.js version is 20 or higher. Running Haraka on older Node.js versions (e.g., Node.js 18) will result in errors.
- deprecated The `graph` plugin was removed in Haraka v3.1.3 due to its dependency on the unmaintained `sqlite3` library. If you relied on this plugin for metrics or visualization, it is no longer available.
- gotcha To bind to privileged ports (e.g., standard SMTP port 25), Haraka must be run with root privileges. Starting Haraka without sufficient permissions will result in `EACCES` errors.
- gotcha Haraka will only accept mail for domains explicitly listed in `config/host_list`. If mail for a domain is rejected or not routed correctly, it's often due to the domain missing from this file.
Install
-
npm install Haraka -
yarn add Haraka -
pnpm add Haraka
Imports
- Haraka CLI Execution
import Haraka from 'Haraka'
haraka -c /path/to/haraka_instance
- Plugin System Integration
import { hook_data } from 'Haraka/plugin_api'// Within a Haraka plugin file (e.g., my_plugin.js): // module.exports.hook_data = function (next, connection, transaction) { ... }; - Configuration Access (Internal)
import { config } from 'Haraka';const config = require('Haraka/config');
Quickstart
#!/usr/bin/env node
// 1. Install Haraka globally (recommended for CLI usage)
// npm install -g Haraka
// 2. Create a directory for your Haraka instance and initialize it
const { execSync } = require('child_process');
const path = require('path');
const fs = require('fs');
const instancePath = path.join(__dirname, 'haraka_test_instance');
console.log(`Initializing Haraka instance at ${instancePath}...`);
execSync(`haraka -i ${instancePath}`, { stdio: 'inherit' });
// 3. (Optional) Configure accepted domains (e.g., 'example.com')
// Edit config/host_list within haraka_test_instance to add your domain.
// For example: echo 'example.com' > ${instancePath}/config/host_list
// 4. (Optional) Configure outbound forwarding (smtp_forward plugin is default)
// Edit config/smtp_forward.ini to specify destination SMTP server.
// Example: echo '[main]\nforward_to=localhost:25000' > ${instancePath}/config/smtp_forward.ini
// 5. Start Haraka (requires root permissions for ports below 1024, like 25)
console.log(`Starting Haraka server (may require sudo for port 25)...`);
console.log(`To stop, use Ctrl+C or kill the process.`);
// In a real scenario, you'd run this from your terminal with 'sudo haraka -c haraka_test_instance'
// For demonstration, we'll just log the command.
console.log(`
Run this command in your terminal: sudo haraka -c ${instancePath}`);
// Example of waiting and then showing how to potentially stop for a script
// In a real application, you'd use a process manager like PM2.
// setTimeout(() => {
// console.log('Demonstration complete. Exiting.');
// // In a real script, you'd send a signal to stop the process.
// }, 15000);