SpamAssassin is an open source spam filter that scores each message against hundreds of tests: header and body patterns, DNS blocklists, SPF and DKIM results, and a Bayesian classifier that learns from your own mail. Messages above a threshold are marked as spam so your mail client or server can file them in a Junk folder, or rejected outright when the score is very high. In this tutorial you will install SpamAssassin on Ubuntu 24.04, connect it to Postfix through spamass-milter, tune its configuration, keep its rules updated and train the Bayesian filter.
Prerequisites
To follow this tutorial, you need:
- A server running Ubuntu 24.04 LTS with Postfix receiving mail for your domain, for example a CubePath VPS.
- A non-root user with
sudoprivileges. - At least 2 GB of RAM. Each SpamAssassin child process uses around 100 MB, and the default configuration runs up to five.
Step 1 - Installing SpamAssassin
On Ubuntu 24.04, SpamAssassin is split into several packages: spamassassin (rules and command line tools), spamd (the scanning daemon) and spamc (the lightweight client that talks to it). spamass-milter connects Postfix to spamd. Install them together:
sudo apt update
sudo apt install spamassassin spamd spamc spamass-milter
Enable and start the daemon:
sudo systemctl enable --now spamd
systemctl status spamd --no-pager
● spamd.service - Perl-based spam filter using text analysis
Loaded: loaded (/usr/lib/systemd/system/spamd.service; enabled; preset: enabled)
Active: active (running)
spamd listens on 127.0.0.1:783 by default and starts its child processes from the options in /etc/default/spamd. The defaults (--create-prefs --max-children 5 --helper-home-dir) are fine for a small or medium server.
Step 2 - Configuring the scoring rules
Site-wide settings go in /etc/spamassassin/local.cf. Open it:
sudo nano /etc/spamassassin/local.cf
The file ships with every option commented out. Add the following at the end:
# Score at which a message is considered spam (default 5.0)
required_score 5.0
# Mark spam in the subject line
rewrite_header Subject [SPAM]
# Add headers instead of wrapping spam in a new message
report_safe 0
# Bayesian classifier with automatic learning
use_bayes 1
bayes_auto_learn 1
required_score 5.0is the upstream default and a good start. Lower values catch more spam but produce more false positives.report_safe 0keeps the original message intact and addsX-Spam-*headers, which makes filtering by header and training much easier than the default attachment format.
Check the configuration for syntax errors:
sudo spamassassin --lint
No output means the configuration is valid. Restart the daemon to load it:
sudo systemctl restart spamd
Step 3 - Keeping the rules updated
Spam changes constantly, so the rules must be updated. The spamassassin package on Ubuntu 24.04 installs a systemd timer, spamassassin-maintenance.timer, that runs sa-update daily. Check that it is active:
systemctl list-timers spamassassin-maintenance.timer --no-pager
NEXT LEFT LAST PASSED UNIT ACTIVATES
Fri 2026-09-26 00:17:00 UTC 13h left - - spamassassin-maintenance.timer spamassassin-maintenance.service
If it is not listed as active, enable it with sudo systemctl enable --now spamassassin-maintenance.timer. Run a first update right away instead of waiting for the timer:
sudo sa-update -v
sudo systemctl restart spamd
sa-update exits with code 0 when it installed new rules and code 1 when the rules were already current; both are fine.
Step 4 - Connecting Postfix through spamass-milter
spamass-milter receives each incoming message from Postfix during the SMTP session, passes it to spamd with spamc, and adds the result as headers. Because it runs during the SMTP session, it can also reject obvious spam before your server accepts it, instead of generating bounces later.
Open its defaults file:
sudo nano /etc/default/spamass-milter
Change the OPTIONS line to:
OPTIONS="-u spamass-milter -i 127.0.0.1 -I -r 15"
-u spamass-milterruns the scan as thespamass-milteruser, so all mail shares one Bayesian database in that user's home directory.-i 127.0.0.1skips mail submitted from the server itself.-Iskips mail from clients that logged in with SMTP AUTH, so your own users' outgoing mail is not scanned.-r 15rejects messages that score 15 or more during the SMTP session. Anything betweenrequired_scoreand 15 is accepted and marked. Remove this option if you prefer never to reject.
On Ubuntu, spamass-milter creates its socket at /var/spool/postfix/spamass/spamass.sock, inside the Postfix chroot, owned by postfix. Restart it and confirm the socket exists:
sudo systemctl restart spamass-milter
sudo ls -l /var/spool/postfix/spamass/
srw-rw---- 1 postfix postfix 0 Sep 25 11:02 spamass.sock
Now tell Postfix to use the milter. Open its main configuration:
sudo nano /etc/postfix/main.cf
Add these lines at the end:
# Spam filtering with SpamAssassin
milter_default_action = accept
smtpd_milters = unix:spamass/spamass.sock
The path is relative to the Postfix queue directory, /var/spool/postfix. If you already use OpenDKIM, keep both milters on one line, for example smtpd_milters = unix:spamass/spamass.sock, inet:localhost:8891, and keep your existing non_smtpd_milters line as it is. milter_default_action = accept lets mail through unscanned if the filter is down, instead of rejecting it.
Validate and reload Postfix:
sudo postfix check
sudo systemctl reload postfix
Step 5 - Testing with GTUBE
GTUBE is a standard test string that every SpamAssassin installation scores as spam, much like the EICAR file for antivirus. First test spamd directly. Create a test message:
nano ~/gtube.eml
Subject: GTUBE test
From: [email protected]
To: you@your_domain
XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X
Pass it through spamc and check the verdict:
spamc -c < ~/gtube.eml
1000.0/5.0
The first number is the score, the second the threshold. A clean message shows something like 0.8/5.0.
To test the whole path through Postfix, send a normal message from an external mailbox (Gmail or Outlook) to an address on your server and look at the headers of the received message:
X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-14) on mail.your_domain
X-Spam-Level:
X-Spam-Status: No, score=-0.1 required=5.0 tests=DKIM_SIGNED,DKIM_VALID,
DKIM_VALID_AU,SPF_PASS autolearn=ham autolearn_force=no version=4.0.0
The mail log shows each scanned message:
sudo journalctl -u spamd -n 20 --no-pager | grep result
spamd[6012]: spamd: result: . 0 - DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,SPF_PASS scantime=0.4,size=4312,user=spamass-milter,...
The first field after result: is Y for spam and . for ham. If you send the GTUBE message from outside with -r 15 enabled, the sender gets a rejection instead.
Step 6 - Training the Bayesian filter
The Bayesian classifier learns what your spam and legitimate mail (ham) look like. It only starts contributing to scores after it has learned from at least 200 spam and 200 ham messages. Auto-learning feeds it slowly; manual training with real mail speeds it up a lot.
The scans run as the spamass-milter user, so the database lives in that user's home directory. Find it:
getent passwd spamass-milter | cut -d: -f6
/var/lib/spamass-milter
The .spamassassin directory is created on the first scan. Create it now in case no mail has been scanned yet, so sa-learn writes the database in the right place:
sudo install -d -o spamass-milter -g spamass-milter -m 700 /var/lib/spamass-milter/.spamassassin
Train it with a folder of spam and a folder of ham, for example the Junk and INBOX folders of a Maildir. Replace the paths with the real ones on your server:
sudo sa-learn --dbpath /var/lib/spamass-milter/.spamassassin --spam /var/mail/vhosts/your_domain/user/.Junk/cur
sudo sa-learn --dbpath /var/lib/spamass-milter/.spamassassin --ham /var/mail/vhosts/your_domain/user/cur
Learned tokens from 214 message(s) (230 message(s) examined)
Because sa-learn ran as root, give the database back to the scanning user:
sudo chown -R spamass-milter:spamass-milter /var/lib/spamass-milter/.spamassassin
Check the counters:
sudo -u spamass-milter sa-learn --dump magic | grep -E "nspam|nham"
0.000 0 214 0 non-token data: nspam
0.000 0 356 0 non-token data: nham
Repeat the training regularly, for example once a week, with messages users moved to Junk and messages they rescued from it.
Step 7 - Allowing and blocking senders
To make sure a trusted sender is never marked as spam, or a persistent one always is, add entries to /etc/spamassassin/local.cf:
# Always accept mail from this sender when relayed by a host in example.com
welcomelist_from_rcvd [email protected] example.com
# Always mark as spam
blocklist_from *@spammy.example
SpamAssassin 4 renamed whitelist_* and blacklist_* options to welcomelist_* and blocklist_*; the old names still work. welcomelist_from_rcvd is safer than a plain welcomelist_from, because it also requires the message to come through a host in the given domain (example.com here), which spammers cannot fake. Run sudo spamassassin --lint and sudo systemctl restart spamd after changing the file.
Troubleshooting
- No
X-Spam-Statusheader on incoming mail. Postfix is not calling the milter. Runpostconf smtpd_milters, check that the socket exists in/var/spool/postfix/spamass/, and look formiltererrors in/var/log/mail.log. connect to Milter service unix:spamass/spamass.sock: No such file or directory.spamass-milteris not running or was started before its directory existed. Restart it withsudo systemctl restart spamass-milter.- Legitimate mail marked as spam. Read the
tests=list in theX-Spam-Statusheader to see which rules fired. Fix the root cause when it is on your side (missing SPF or DKIM on the sender, for example), train the message as ham, or add awelcomelist_from_rcvdentry. - High memory use. Lower
--max-childrenin/etc/default/spamd, for example to 3, and restartspamd.
Conclusion
Incoming mail on your server is now scored by SpamAssassin during the SMTP session, very obvious spam is rejected before it is accepted, rules update daily, and the Bayesian filter learns from your own mail. As next steps, add a Sieve rule in Dovecot that moves messages with X-Spam-Flag: YES to the Junk folder, and review your own outgoing setup with How to Stop Your Server's Emails from Going to Spam.
