Rspamd is a fast, modular spam filtering system that scores each message using many checks (SPF, DKIM, DMARC, DNS blocklists, a Bayesian classifier, fuzzy hashes and your own rules) and then decides on an action such as accepting, greylisting, marking or rejecting it. In this tutorial you will install Rspamd on an Ubuntu 24.04 mail server that already runs Postfix, connect it as a milter, store its learning data in Redis, sign outgoing mail with DKIM, tune the action thresholds, and add custom rules.
Prerequisites
To follow this tutorial, you will need:
- A server running Ubuntu 24.04 LTS, for example a CubePath VPS, with at least 2 GB of RAM.
- A non-root user with
sudoprivileges. - A working Postfix installation that receives and sends mail for
your_domain. Remove any other filter such as OpenDKIM or SpamAssassin from the Postfix milter chain first, so they do not duplicate Rspamd's work. - Access to your domain's DNS zone to publish a DKIM record.
Step 1 - Installing Rspamd and Redis
The Rspamd project recommends its own repository over the Ubuntu package, which lags behind upstream. Install the tools needed to add it:
sudo apt update
sudo apt install -y lsb-release wget gpg
Download the signing key into /etc/apt/keyrings and add the repository:
sudo install -m 0755 -d /etc/apt/keyrings
wget -qO- https://rspamd.com/apt-stable/gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/rspamd.gpg > /dev/null
echo "deb [signed-by=/etc/apt/keyrings/rspamd.gpg] https://rspamd.com/apt-stable/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/rspamd.list
Install Rspamd and Redis. Redis stores Bayes statistics, greylisting state, rate limits and other learned data:
sudo apt update
sudo apt install -y rspamd redis-server
Both services start automatically. Check them:
systemctl is-active rspamd redis-server
active
active
Rspamd listens on three local ports: 11332 for the milter (proxy worker), 11333 for the scanner (normal worker) and 11334 for the controller and web interface:
sudo ss -tlpn | grep rspamd
Step 2 - Understanding the configuration layout
Never edit the files in /etc/rspamd/ directly: package upgrades overwrite them. Rspamd reads two override directories instead:
/etc/rspamd/local.d/: settings in a file here are merged with the default file of the same name. Use this in almost all cases./etc/rspamd/override.d/: settings here replace the defaults for that section entirely.
After each change, validate the configuration before reloading:
sudo rspamadm configtest
syntax OK
Step 3 - Connecting Redis
Tell Rspamd where Redis is. Every module that needs storage uses this setting:
sudo nano /etc/rspamd/local.d/redis.conf
servers = "127.0.0.1:6379";
Rspamd data can grow over time, so give Redis a memory limit and an eviction policy that drops the least recently used keys when it is reached. Open the Redis configuration:
sudo nano /etc/redis/redis.conf
Find the maxmemory and maxmemory-policy directives, uncomment them and set:
maxmemory 512mb
maxmemory-policy volatile-ttl
volatile-ttl evicts keys that have an expiry, starting with those closest to expiring, which suits Rspamd's Bayes tokens and greylisting entries. Restart Redis and reload Rspamd:
sudo systemctl restart redis-server
sudo rspamadm configtest && sudo systemctl reload rspamd
Step 4 - Connecting Rspamd to Postfix
Postfix passes each message to Rspamd through the milter protocol on port 11332. Add the milter settings with postconf, which edits /etc/postfix/main.cf safely:
sudo postconf -e 'smtpd_milters = inet:127.0.0.1:11332'
sudo postconf -e 'non_smtpd_milters = inet:127.0.0.1:11332'
sudo postconf -e 'milter_default_action = accept'
sudo postconf -e 'milter_protocol = 6'
smtpd_milters covers mail received over SMTP, non_smtpd_milters covers mail submitted locally (for example by sendmail from a web application), and milter_default_action = accept keeps mail flowing if Rspamd is temporarily down.
By default Rspamd adds few headers to messages. Make it add a summary of the scan and an Authentication-Results header, which are useful for debugging:
sudo nano /etc/rspamd/local.d/milter_headers.conf
use = ["x-spamd-result", "x-spam-status", "authentication-results"];
Apply both changes:
sudo rspamadm configtest && sudo systemctl reload rspamd
sudo systemctl reload postfix
Send a message to a local mailbox from an external account, open it, and look for the new headers:
X-Spamd-Result: default: False [-0.10 / 15.00];
R_SPF_ALLOW(-0.20)[+ip4:203.0.113.10];
DMARC_POLICY_ALLOW(-0.50)[example.com,none];
...
X-Spam-Status: No, score=-0.10
Step 5 - Testing with GTUBE
GTUBE is a standard test string that every spam filter must classify as spam. Create a test message containing it:
nano ~/gtube.eml
From: [email protected]
To: alice@your_domain
Subject: GTUBE test
XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X
Scan it with rspamc, the command-line client:
rspamc < ~/gtube.eml
Results for file: stdin
Action: reject
Spam: true
Score: 0.00 / 15.00
Symbol: GTUBE (0.00)
The reject action confirms Rspamd is scanning and acting on results.
Step 6 - Tuning actions and greylisting
Rspamd maps the total score to an action. The defaults are sensible; change them in local.d/actions.conf only after you have looked at real traffic:
sudo nano /etc/rspamd/local.d/actions.conf
greylist = 4;
add_header = 6;
reject = 15;
- greylist: temporarily defer the message. Legitimate servers retry; many spam bots do not.
- add_header: deliver the message with spam headers, so the mail client or a Sieve rule can file it into Junk.
- reject: refuse the message during the SMTP conversation.
Greylisting is performed by the greylist module and stores its state in Redis. To shorten the delay senders see and keep entries for a week, create:
sudo nano /etc/rspamd/local.d/greylist.conf
timeout = 2min;
expire = 7d;
timeout is how long a new sender must wait before a retry is accepted, and expire is how long a sender who passed is remembered. Authenticated users and local submissions are not greylisted.
Reload to apply:
sudo rspamadm configtest && sudo systemctl reload rspamd
Step 7 - Training the Bayes classifier
The Bayesian classifier learns from your own mail and becomes one of the most accurate signals once trained. With Redis configured, enable automatic learning from high-confidence results:
sudo nano /etc/rspamd/local.d/classifier-bayes.conf
backend = "redis";
autolearn = true;
With autolearn = true, Rspamd learns messages it rejects as spam and messages with strongly negative scores as ham. The classifier only starts contributing to scores after it has learned at least 200 spam and 200 ham messages.
Speed this up by feeding it existing mail. rspamc accepts files and whole Maildir directories:
rspamc learn_spam /home/alice/Maildir/.Junk/cur/
rspamc learn_ham /home/alice/Maildir/cur/
Train only on folders you have checked by hand; a mislabeled corpus teaches the classifier the wrong thing. Check the counters:
rspamc stat | grep -i bayes
Statfile: BAYES_SPAM type: redis; length: 0; free blocks: 0; total blocks: 0; free: 0.00%; learned: 215; users: 1; languages: 0
Statfile: BAYES_HAM type: redis; length: 0; free blocks: 0; total blocks: 0; free: 0.00%; learned: 1340; users: 1; languages: 0
rspamc talks to the controller on 127.0.0.1:11334, which trusts local connections by default, so these commands need no password when run on the server.
Step 8 - Signing outgoing mail with DKIM
Rspamd can sign mail from authenticated users and local submissions, replacing a separate OpenDKIM service. Create a directory for the keys that only Rspamd can read (the package runs as the _rspamd user):
sudo install -d -o _rspamd -g _rspamd -m 0750 /var/lib/rspamd/dkim
Generate a 2048-bit key with the selector mail. The command writes the private key to the path given with -k and prints the DNS record to standard output, which you save alongside it:
sudo rspamadm dkim_keygen -s mail -d your_domain -b 2048 -k /var/lib/rspamd/dkim/your_domain.mail.key | sudo tee /var/lib/rspamd/dkim/your_domain.mail.txt
sudo chown _rspamd:_rspamd /var/lib/rspamd/dkim/your_domain.mail.key
sudo chmod 0640 /var/lib/rspamd/dkim/your_domain.mail.key
The output looks like this:
mail._domainkey IN TXT ( "v=DKIM1; k=rsa; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
"...IDAQAB" ) ;
Publish it as a TXT record named mail._domainkey in your DNS zone, joining the quoted parts into one value: v=DKIM1; k=rsa; p=MIIBIjANBgkq...IDAQAB.
Configure the signing module. The $domain and $selector variables are expanded at signing time, so one line covers every domain whose key follows the naming pattern:
sudo nano /etc/rspamd/local.d/dkim_signing.conf
path = "/var/lib/rspamd/dkim/$domain.$selector.key";
selector = "mail";
allow_username_mismatch = true;
allow_username_mismatch lets users whose login name is not their full email address sign mail for the domain. ARC signing, which preserves authentication results when mail is forwarded, uses the same key. Copy the file to enable it:
sudo cp /etc/rspamd/local.d/dkim_signing.conf /etc/rspamd/local.d/arc.conf
sudo rspamadm configtest && sudo systemctl reload rspamd
Once the DNS record has propagated, check it:
dig +short TXT mail._domainkey.your_domain
Send a message from an authenticated account to an external mailbox and confirm dkim=pass header.d=your_domain in the received message's Authentication-Results header.
Step 9 - Adding custom rules with multimap
The multimap module is the simplest way to add your own rules: each rule matches part of a message against a list in a map file and adds a score. Create a directory for your maps:
sudo mkdir -p /etc/rspamd/local.d/maps.d
Create a map of regular expressions to match in the Subject header:
sudo nano /etc/rspamd/local.d/maps.d/subject_blocklist.map
/make money fast/i
/you have won/i
/claim your prize/i
Create a map of IP addresses or networks of trusted partners whose mail should never be flagged, such as an internal relay:
sudo nano /etc/rspamd/local.d/maps.d/trusted_ips.map
198.51.100.25
203.0.113.0/24
Define the rules that use them:
sudo nano /etc/rspamd/local.d/multimap.conf
LOCAL_SUBJECT_BLOCKLIST {
type = "header";
header = "Subject";
regexp = true;
map = "${LOCAL_CONFDIR}/local.d/maps.d/subject_blocklist.map";
score = 6.0;
description = "Subject matches the local blocklist";
}
LOCAL_TRUSTED_IP {
type = "ip";
map = "${LOCAL_CONFDIR}/local.d/maps.d/trusted_ips.map";
score = -10.0;
description = "Sender IP is a trusted partner";
}
Allowlist by IP rather than by sender address: a From address is trivially forged, an IP address is not. Map files are reloaded automatically when they change, so you can add entries later without reloading Rspamd. Apply the new rules:
sudo rspamadm configtest && sudo systemctl reload rspamd
Test the subject rule with a sample message:
printf 'From: [email protected]\nTo: alice@your_domain\nSubject: You have WON a prize\n\nHello\n' | rspamc
Symbol: LOCAL_SUBJECT_BLOCKLIST (6.00)
Step 10 - Securing the web interface
The web interface shows scan history, symbol statistics and lets you train messages or change scores. It listens only on 127.0.0.1:11334, but it still needs a password. Generate a hash:
rspamadm pw
Type a strong password twice. The command prints a hash that starts with $2$. Put it in the controller configuration:
sudo nano /etc/rspamd/local.d/worker-controller.inc
password = "$2$your_generated_hash";
Reload Rspamd:
sudo rspamadm configtest && sudo systemctl reload rspamd
To open the interface, create an SSH tunnel from your computer and browse to http://localhost:11334:
ssh -L 11334:127.0.0.1:11334 your_user@your_server_ip
Keeping the controller on localhost and reaching it through SSH avoids exposing it to the internet at all.
Troubleshooting
Messages are delivered without X-Spamd-Result headers: confirm Postfix has the milter configured with postconf smtpd_milters, and that Rspamd is listening with sudo ss -tlpn | grep 11332. Check the Postfix log for milter errors with sudo grep -i milter /var/log/mail.log.
Bayes symbols never appear: the classifier needs 200 spam and 200 ham messages before it scores anything. Check rspamc stat and make sure Redis is reachable with redis-cli ping.
Too many false positives: find which symbols drive the score with rspamc on a saved copy of the message, or in the history tab of the web interface. Lower the score of the specific symbol from the Symbols tab of the web interface, or allowlist the sender's IP with the multimap rule, rather than raising the reject threshold for everyone.
DKIM signing does not happen: Rspamd only signs mail from authenticated users and local networks, and only when the key file exists at the expanded path. Check journalctl -u rspamd for dkim_signing messages and verify the key is readable by _rspamd.
Conclusion
Your Postfix server now filters mail through Rspamd with Redis-backed Bayes learning, greylisting, tuned actions, DKIM and ARC signing, custom multimap rules and a password-protected web interface. As next steps, let users train the classifier by moving mail in and out of Junk with Dovecot's IMAPSieve, set up a local fuzzy storage worker to catch repeated spam campaigns, and publish a strict DMARC policy once all your outgoing mail passes DKIM.
