Spam Filtering with Rspamd

August 25, 2026

Rspamd is a spam filtering system that replaces SpamAssassin with better performance and a modern architecture. It processes incoming mail through multiple modules — Bayes statistical classification, DNS blacklists, greylisting, rate limiting, and content analysis — and assigns a spam score. Postfix integrates with Rspamd via the milter protocol. This post covers installing Rspamd on Gentoo, configuring its modules, connecting it to Redis for statistical storage, and integrating with Postfix.

Prerequisites

Installing Rspamd

On Gentoo

emerge -av mail-filter/rspamd

Rspamd on Gentoo installs with sensible defaults. The configuration lives in /etc/rspamd/.

Understanding Rspamd's Configuration Layout

Rspamd uses a layered configuration system:

Path Purpose
/etc/rspamd/rspamd.conf Main configuration (rarely modified)
/etc/rspamd/modules.d/ Default module configurations
/etc/rspamd/local.d/ Local overrides (your customizations)
/etc/rspamd/override.d/ Hard overrides (replace entire blocks)

Always put your changes in /etc/rspamd/local.d/. Files here are merged with the defaults. Never edit files in modules.d/ directly — they get overwritten on updates.

Configuring Redis

Rspamd uses Redis for Bayes classification data, greylisting state, rate limit counters, and fuzzy hash storage. Create /etc/rspamd/local.d/redis.conf:

servers = "127.0.0.1:6379";

This tells all Rspamd modules to use the local Redis instance.

Configuring the Web Interface

Rspamd includes a web dashboard for monitoring spam statistics and training the Bayes classifier. Configure it in /etc/rspamd/local.d/worker-controller.inc:

password = "$2$your-bcrypt-hash-here";
bind_socket = "127.0.0.1:11334";

Generate the password hash:

rspamadm pw --encrypt

Enter your password when prompted. Copy the output hash (starting with $2$) into the configuration.

The controller binds to localhost only. Access it through an SSH tunnel or reverse proxy.

Configuring the Milter Worker

The milter worker is how Postfix communicates with Rspamd. Create /etc/rspamd/local.d/worker-proxy.inc:

bind_socket = "127.0.0.1:11332";
milter = yes;
timeout = 120s;
upstream "local" {
    default = yes;
    self_scan = yes;
}

Configuring DKIM Signing

Rspamd can handle DKIM signing instead of (or in addition to) OpenDKIM. If you prefer Rspamd for signing, create /etc/rspamd/local.d/dkim_signing.conf:

allow_envfrom_empty = true;
allow_hdrfrom_mismatch = false;
allow_hdrfrom_mismatch_local = true;
allow_username_mismatch = true;
sign_authenticated = true;
sign_local = true;
use_domain = "header";
use_esld = true;

domain {
    example.com {
        path = "/etc/opendkim/keys/example.com/mail.private";
        selector = "mail";
    }
    example.org {
        path = "/etc/opendkim/keys/example.org/mail.private";
        selector = "mail";
    }
}

If you're using OpenDKIM for signing (configured in the DKIM post), you can skip this and let Rspamd handle verification only.

Configuring Bayes Classification

Bayes classification learns from your mail patterns. Create /etc/rspamd/local.d/classifier-bayes.conf:

backend = "redis";
autolearn = true;
min_learns = 200;
expire = 8640000;

autolearn = true lets Rspamd automatically learn from messages with very high or very low spam scores. min_learns requires at least 200 learned messages before Bayes scores affect filtering.

Training Bayes

Train Rspamd with known spam and ham (legitimate mail):

# Train as spam
rspamc learn_spam /path/to/spam-message.eml

# Train as ham
rspamc learn_ham /path/to/ham-message.eml

You can also train from an entire Maildir:

rspamc learn_spam /var/vmail/example.com/user/Maildir/.Junk/cur/
rspamc learn_ham /var/vmail/example.com/user/Maildir/cur/

Configuring Rate Limiting

Rate limiting prevents a single sender or IP from flooding the server. Create /etc/rspamd/local.d/ratelimit.conf:

rates {
  to = {
    symbol = "RATELIMIT_TO";
    bucket {
      burst = 100;
      rate = "10 / 1min";
    }
  }
  bounce_to = {
    symbol = "RATELIMIT_BOUNCE_TO";
    bucket {
      burst = 5;
      rate = "2 / 5min";
    }
  }
}

Rate limit state is stored in Redis. The bounce_to rate is stricter because legitimate bounce traffic is low — a flood of bounces usually indicates a backscatter attack.

Configuring DNS Blacklists

Rspamd queries DNS-based blacklists (RBLs) by default. The built-in configuration checks Spamhaus, SORBS, and other major lists. To customize which lists are checked, create /etc/rspamd/local.d/rbl.conf:

rbls {
  spamhaus_zen {
    symbol = "RBL_SPAMHAUS_ZEN";
    rbl = "zen.spamhaus.org";
    ipv6 = true;
    returncodes {
      RBL_SPAMHAUS_SBL = "127.0.0.2";
      RBL_SPAMHAUS_CSS = "127.0.0.3";
      RBL_SPAMHAUS_XBL = "127.0.0.4/30";
      RBL_SPAMHAUS_PBL = "127.0.0.10/31";
    }
  }
}

If you run a local DNS resolver (like Unbound), RBL queries are fast and don't hit external rate limits.

Configuring Greylisting

Greylisting temporarily rejects mail from unknown senders. Legitimate servers retry after a delay; spammers typically don't. Create /etc/rspamd/local.d/greylist.conf:

enabled = true;
expire = 86400;
timeout = 300;

timeout = 300 means the greylist entry expires after 5 minutes of the initial rejection. The sender must retry after 5 minutes. expire = 86400 means known senders are remembered for 24 hours.

Configuring Actions

Actions determine what happens at each spam score threshold. Create /etc/rspamd/local.d/actions.conf:

reject = 15;
add_header = 6;
greylist = 4;
  • Score >= 15: Reject the message outright
  • Score >= 6: Accept but add a spam header (X-Spam: Yes)
  • Score >= 4: Greylist the sender

These thresholds are conservative. Adjust after monitoring for false positives.

Headers Added by Rspamd

Rspamd adds headers to every processed message:

  • X-Spam: Yes — message scored above the add_header threshold
  • X-Spamd-Result — detailed breakdown of the spam score
  • X-Spamd-Bar — visual spam score indicator

The Sieve script from the Dovecot post files messages with X-Spam: Yes into the Junk folder.

Integrating with Postfix

Add Rspamd as a milter in Postfix's main.cf. If you're also using OpenDKIM for DKIM signing, chain both milters together — Rspamd processes first (spam check), then OpenDKIM signs:

milter_protocol = 6
milter_default_action = accept
milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}
smtpd_milters = inet:127.0.0.1:11332, inet:127.0.0.1:8891
non_smtpd_milters = inet:127.0.0.1:11332, inet:127.0.0.1:8891

Important: If you configure milters in multiple places (e.g., main.cf and a separate role for OpenDKIM), the last smtpd_milters line wins. Make sure both milters appear in a single declaration. A common mistake is having a DKIM-signing role overwrite the milter line with only inet:127.0.0.1:8891, silently removing Rspamd from the chain. If spam filtering appears to stop working, check postconf smtpd_milters to verify both milters are listed.

If you only use Rspamd (no OpenDKIM):

smtpd_milters = inet:127.0.0.1:11332
non_smtpd_milters = inet:127.0.0.1:11332

Restart Postfix:

rc-service postfix restart

DNS Resolver Requirements

Rspamd queries DNS-based blocklists (Spamhaus, SORBS, URIBL) for every incoming message. These RBL providers block queries from public or shared DNS resolvers. If the server uses a hosting provider's resolver or forwards to 8.8.8.8/1.1.1.1, Spamhaus returns an error code and Rspamd disables the blocklist entirely — with no obvious warning beyond a log message.

A local recursive resolver like Unbound is required for RBL queries to work. Verify after setup:

# Should return specific blocklist codes like 127.0.0.x — NOT empty or an error
dig @127.0.0.1 +short zen.spamhaus.org

Starting and Enabling Rspamd

rc-update add rspamd default
rc-service rspamd start

Verifying the Installation

Check Rspamd Is Running

ss -lnt sport = :11332
ss -lnt sport = :11334

Port 11332 is the milter worker, port 11334 is the web interface.

Test the Web Interface

curl -s http://127.0.0.1:11334/stat

This returns JSON with processing statistics.

Test Spam Detection

Use the GTUBE test string (Generic Test for Unsolicited Bulk Email) to verify Rspamd detects spam:

rspamc < /usr/share/rspamd/gtube.eml

Or send a message containing the GTUBE string through Postfix and verify it gets the spam header.

Check Redis Integration

redis-cli KEYS "rs_*" | head -10

Rspamd stores its data in Redis with rs_ prefixed keys. If keys exist, the integration is working.

Check Bayes Statistics

rspamc stat

This shows the number of learned ham and spam messages, and whether the Bayes classifier is active. Until min_learns (200) is reached for both ham and spam, Bayes will report as inactive.

Test a Specific Message

Scan a single message file without going through Postfix:

rspamc < /path/to/message.eml

This outputs the full score breakdown, showing which modules triggered and their individual scores.

Summary

After completing these steps:

  • Rspamd filters incoming mail through multiple detection modules
  • Bayes classification learns from spam and ham patterns via Redis
  • Greylisting temporarily rejects unknown senders
  • The web interface provides monitoring and manual training
  • Postfix forwards all mail through Rspamd via milter protocol
  • Spam messages get headers that Dovecot Sieve files into Junk

The next step is adding ClamAV antivirus scanning to catch malware in attachments.