Haraka is a high-performance SMTP server written in Node.js, designed to handle massive email volumes with a plugin-based architecture. Unlike Postfix or Exim, Haraka uses an event-driven model that excels at processing millions of messages per day while maintaining low resource usage. This guide covers deployment, plugin configuration, and production hardening.
Why Haraka?
- Performance — handles 10,000+ messages per second on modest hardware
- Plugin system — over 60 built-in plugins for auth, spam filtering, DKIM, rate limiting
- Node.js ecosystem — write custom plugins in JavaScript
- Modern architecture — async I/O, perfect for high-concurrency email processing
Installation
# Install Node.js 20+ and npm
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -
sudo apt install nodejs
# Install Haraka
sudo npm install -g Haraka
haraka -i /etc/haraka # Initialize config directory
Core Configuration
# /etc/haraka/config/smtp.ini
[main]
listen=0.0.0.0:25,0.0.0.0:587
nodes=cpus # Use all CPU cores
daemonize=true
[headers]
add_received=true
clean_auth_results=true
# /etc/haraka/config/me
mail.example.com
# /etc/haraka/config/host_list
example.com
Essential Plugins
# /etc/haraka/config/plugins
# Authentication
auth/flat_file
# or auth/auth_ldap for LDAP
# Security
tls
dkim_sign
spf
dmarc
# Anti-spam
data.headers
mail_from.is_resolvable
rcpt_to.in_host_list
limit
clamd # ClamAV integration
# Delivery
queue/smtp_forward
TLS Configuration
# /etc/haraka/config/tls.ini
key=/etc/letsencrypt/live/mail.example.com/privkey.pem
cert=/etc/letsencrypt/live/mail.example.com/fullchain.pem
dhparam=/etc/haraka/config/dhparams.pem
ciphers=ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256
DKIM Signing
# /etc/haraka/config/dkim_sign.ini
disabled=false
selector=mail
domain=example.com
headers_to_sign=from:to:subject:date:message-id
# Place private key at:
# /etc/haraka/config/dkim/example.com/mail.private
Rate Limiting
# /etc/haraka/config/limit.ini
[outbound]
enabled=true
max_undelivered=500
[recipients]
max=100 # Per message
max_count=500 # Per connection
[rate_conn]
max=30 # Connections per minute per IP
[rate_rcpt]
max=100 # Recipients per minute per IP
Custom Plugin Example
// /etc/haraka/plugins/custom_filter.js
exports.hook_data = function (next, connection) {
const transaction = connection.transaction;
transaction.add_body_filter('', function (content_type, encoding, body_buffer) {
// Log message size
connection.loginfo(this, `Message size: ${body_buffer.length} bytes`);
return body_buffer;
});
next();
};
exports.hook_queue = function (next, connection) {
const from = connection.transaction.mail_from.original;
const rcpt = connection.transaction.rcpt_to.map(r => r.original);
connection.loginfo(this, `Queuing: ${from} -> ${rcpt.join(', ')}`);
next(OK);
};
Forwarding to Dovecot via LMTP
# /etc/haraka/config/smtp_forward.ini
host=127.0.0.1
port=24 # Dovecot LMTP port
enable_tls=false
auth_type=
Running as a Service
# /etc/systemd/system/haraka.service
[Unit]
Description=Haraka SMTP Server
After=network.target
[Service]
Type=forking
ExecStart=/usr/bin/haraka -c /etc/haraka
Restart=on-failure
User=haraka
[Install]
WantedBy=multi-user.target
Monitoring
# Haraka logs to syslog by default
sudo journalctl -u haraka -f
# Enable stats plugin for Prometheus metrics
# Add to plugins: metrics/prometheus
# /etc/haraka/config/prometheus.ini
port=9904
Best Practices
- Use
nodes=cpusto utilize all CPU cores for maximum throughput - Enable TLS for all connections — both inbound (port 25) and submission (port 587)
- Implement rate limiting to prevent abuse from compromised accounts
- Use the queue/smtp_forward plugin to deliver to Dovecot LMTP for local delivery
- Write custom plugins for business-specific email processing logic
- Monitor with the Prometheus metrics plugin for production visibility