Haraka SMTP Server

raw JSON →
3.1.5 verified Sun Apr 19 auth: no javascript

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.

error 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).
fix
Run Haraka with sudo: sudo haraka -c /path/to/haraka_instance.
error 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.
fix
Stop the conflicting process, or configure Haraka to listen on a different port by editing config/smtp.ini's listen setting.
error 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.
fix
If 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.
error 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.
fix
Add the recipient's domain (e.g., example.com) to the config/host_list file in your Haraka instance directory and restart Haraka.
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.
fix Review your existing configuration files (e.g., `haproxy_hosts`, `smtpgreeting.ini`, `databytes.ini`) and migrate their settings to the appropriate sections within `config/connection.ini`. Refer to the `connection.ini` documentation for the new structure.
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.
fix Upgrade your Node.js environment to version 20 or newer. It is recommended to use an LTS (Long Term Support) release of Node.js.
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.
fix Remove `graph` from your `config/plugins` file. Explore alternative monitoring and logging solutions that integrate with Haraka's standard logging (e.g., by piping logs to external analysis tools).
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.
fix Always use `sudo haraka -c /path/to/instance` when running Haraka on ports below 1024. Alternatively, configure Haraka to listen on a non-privileged port (e.g., 2525) for testing, or use capabilities/setuid wrappers in production if direct root is undesired.
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.
fix Ensure that all domains for which Haraka should accept or process mail are listed one per line in the `config/host_list` file within your Haraka instance directory. Restart Haraka after making changes to configuration files.
npm install Haraka
yarn add Haraka
pnpm add Haraka

Demonstrates the typical workflow for installing Haraka globally, initializing a new server instance, and preparing it for startup as an SMTP server, including notes on configuration and permissions.

#!/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);