2026年11月12日
2026年11月12日
SpamAssassin Setup: Postfix Integration, local.cf and Bayes
This article is a localized edition of the Japanese original. Read the Japanese original
SpamAssassin is a filter that scores incoming mail against rules covering headers, body, DNS blocklists, Bayesian learning and more, and classifies a message as spam when its total score exceeds a threshold. The verdict is only added as headers, so the actual sorting or rejection depends on how you integrate it with Postfix and on your mailbox settings. This article covers a Debian/Ubuntu server, from installation through the three ways to integrate with Postfix, the main local.cf settings, rule updates, training and testing.
The current SpamAssassin release line is 4.0 (as of October 2026). Package and service names differ between distribution versions, so where relevant the article also shows how to check them.
Decide on the integration method first
There are three main ways to connect SpamAssassin to Postfix. Which one you choose changes both the packages you install and where you configure things.
- spamd + spamc (content_filter): Postfix passes received mail through a pipe to
spamc; the residentspamdscores it and then hands it back for delivery. The setup is simple and well suited to getting something running first. Because scoring happens after the mail is received, you cannot reject spam at reception time; instead you add headers and deliver. - milter (spamass-milter): Mail is scored during the SMTP conversation, so high-scoring mail can be rejected at reception time. The sender is told about the rejection, which is better-behaved than accepting mail and then discarding it.
- amavis (amavisd-new): Scans mail together with antivirus tools such as ClamAV in one pass. amavis calls SpamAssassin directly as a library, so it does not use
spamd.
As a rule of thumb: amavis if you also want virus scanning, milter if rejecting at reception matters, and spamd + spamc otherwise.
Installation and starting spamd
Here we use the spamd + spamc setup.
sudo apt update
sudo apt install spamassassin spamc
# On Debian 12 / Ubuntu 24.04 and later, spamd is a separate package
sudo apt install spamd
# Check the service name (spamd.service or spamassassin.service)
systemctl list-unit-files | grep -i spam
On Debian 12, Ubuntu 24.04 and later, the daemon is split into the spamd package and the service is also called spamd. On older releases, the daemon is included in the spamassassin package and the service is called spamassassin. Start it using the name shown by the command above.
sudo systemctl enable --now spamd
sudo systemctl status spamd
By default spamd listens on port 783 on 127.0.0.1. There is no need to expose it externally, so do not open port 783 in your firewall. On Debian-based systems, spamd does its work as the user debian-spamd.
Basic local.cf settings
Site-wide settings go in /etc/spamassassin/local.cf. Copy the original file before editing so you can roll back if something goes wrong.
sudo cp /etc/spamassassin/local.cf /etc/spamassassin/local.cf.bak
# /etc/spamassassin/local.cf
# Total score at which mail is classified as spam (default 5.0)
required_score 5.0
# 0 = leave the original message as-is and only add headers
report_safe 0
# String prepended to the subject of mail classified as spam
rewrite_header Subject [SPAM]
# Use Bayesian learning and enable auto-learning
use_bayes 1
bayes_auto_learn 1
# Detect the character encoding before parsing the body (for Japanese mail)
normalize_charset 1
# If your own relay server sits in front, specify its address
# trusted_networks 192.0.2.0/24
If you set report_safe to 1, spam is replaced by a new message that wraps the original as an attachment. If you sort mail by headers, 0 is easier to work with because the original message is easy to retrieve after a false positive. Lowering required_score increases both detections and false positives, so start at 5.0 and adjust while watching the score in the X-Spam-Status header.
trusted_networks sets how far along the relay chain you trust. In a setup with a mail gateway in front, if you leave it unset, blocklist lookups are run against your own server's IP and the results are skewed. If a single server receives mail directly, you can usually leave it unset.
Allow and block lists, and custom rules
To always let a particular sender through, or always treat one as spam, write the following. In the 4.0 series the names changed to welcomelist_from / blocklist_from. The old whitelist_from / blacklist_from still work for compatibility, but use the new names for anything you write now.
welcomelist_from *@partner.example.com
blocklist_from *@spam-sender.example.net
# Custom rule: add 3 points to mail whose subject contains a specific phrase
header LOCAL_SUBJ_OFFER Subject =~ /limited time offer/i
describe LOCAL_SUBJ_OFFER Subject contains a typical spam phrase
score LOCAL_SUBJ_OFFER 3.0
Note that welcomelist_from only looks at the displayed sender address, so it is vulnerable to spoofing. If you can use welcomelist_from_spf or welcomelist_from_dkim, which combine it with SPF or DKIM verification results, those are safer.
After changing the settings, check the syntax and then have spamd reload them. --lint prints nothing if there are no problems.
sudo spamassassin --lint
sudo systemctl reload spamd
Integrating with Postfix (content_filter)
Edit /etc/postfix/master.cf. Back it up before editing as well. Add content_filter to the existing smtp inet line, and define a service for the filter at the end of the file.
smtp inet n - y - - smtpd
-o content_filter=spamassassin
spamassassin unix - n n - - pipe
user=debian-spamd argv=/usr/bin/spamc -e /usr/sbin/sendmail -oi -f ${sender} ${recipient}
Mail received on port 25 is passed to the spamassassin service, where spamc adds the scoring headers and hands it back for delivery via sendmail. The returned mail goes through the local submission path rather than SMTP, so it is not filtered again and does not loop. Leave the fifth column (chroot) of the smtp inet line at its default value. Note that by default spamc passes mail larger than 500KB through without scoring it.
sudo postfix check
sudo systemctl reload postfix
Key points when using milter or amavis
For spamass-milter, on Debian-based systems install the spamass-milter package and specify the socket with smtpd_milters in Postfix's main.cf. The Debian package creates the socket under Postfix's queue directory by default, so the setting looks like this.
smtpd_milters = unix:/spamass/spamass.sock
milter_default_action = accept
milter_default_action = accept keeps mail being accepted when the milter is down. Without it, a spamd failure translates directly into mail reception stopping. The rejection threshold is set with a startup option (-r) in /etc/default/spamass-milter. You may need to add the Postfix user to a group so that it can access the socket, so check the package README as well.
For amavis, uncomment the @bypass_spam_checks_maps line in /etc/amavis/conf.d/15-content_filter_mode to enable spam checks. In this case, the scores for adding headers and for classifying spam come from amavis's $sa_tag_level_deflt, $sa_tag2_level_deflt and $sa_kill_level_deflt, and required_score in local.cf has no effect on the verdict. If you changed local.cf and the behavior didn't change, check this point.
Updating rules (sa-update)
SpamAssassin rules are updated regularly. To update manually, run the following.
sudo sa-update && sudo systemctl reload spamd
sa-update returns exit code 0 when updates were installed and 1 when there were none. Written as above, spamd is reloaded only when there was an update. A code of 2 or higher is an error, so check for network or GPG verification problems. Packages in many distributions include a scheduled job, so first check with systemctl list-timers | grep -i spam or your cron settings whether one is already running.
DNS blocklists and the resolver
SpamAssassin's accuracy depends heavily on rules that look up the sender IP and URLs in the message body in DNS blocklists. However, lookups through large public resolvers such as Google Public DNS are often refused by the blocklists, in which case a rule such as URIBL_BLOCKED fires and no lookup result is returned.
Installing a caching DNS resolver such as Unbound on the server and having SpamAssassin use it solves this. If you don't want to change the system-wide resolver, you can add dns_server 127.0.0.1 to local.cf so that only SpamAssassin uses the local resolver.
Bayesian learning
The Bayesian filter is not used for scoring until it has learned at least 200 spam and 200 legitimate messages each. Auto-learning alone tends to become skewed, so regularly training it on mail that users moved to their Junk folder improves accuracy.
The learning database is created in .spamassassin under the home directory of the user that does the scoring. In a spamd + spamc setup with user=debian-spamd, training only affects scoring if you train the debian-spamd database.
# Check the home directory of debian-spamd
getent passwd debian-spamd
# Train (adjust the path to match the home directory)
sudo sa-learn --dbpath /var/lib/spamassassin/.spamassassin --spam /home/user1/Maildir/.Junk/cur
sudo sa-learn --dbpath /var/lib/spamassassin/.spamassassin --ham /home/user1/Maildir/cur
sudo chown -R debian-spamd: /var/lib/spamassassin/.spamassassin
# Check how many messages have been learned (nspam / nham)
sudo -u debian-spamd sa-learn --dump magic
Training as root makes the database files owned by root, which stops spamd from writing to them, so the ownership is restored at the end.
Sorting and testing
To move mail classified as spam to the Junk folder, if you use Dovecot, sort it with Sieve by checking the X-Spam-Flag header.
require ["fileinto"];
if header :contains "X-Spam-Flag" "YES" {
fileinto "Junk";
}
For testing, use the test string (GTUBE) designed to score high in any filter.
printf 'Subject: test\n\nXJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X\n' | spamc -R
If the score and the matched rules are displayed, spamd is working. Next, send mail from an external address to your own server and check whether the received message has an X-Spam-Status header. If it doesn't, check /var/log/mail.log to see whether the mail passed through the spamassassin service, and check the spamd log for errors.
Summary
- There are three integration methods: the simple spamd + spamc, milter that can reject at reception, and amavis that combines spam and virus scanning. With amavis,
required_scoreinlocal.cfhas no effect. - On Debian 12 / Ubuntu 24.04 and later, spamd is a separate package. Check the service name with
systemctl list-unit-files. - Start
local.cfwithrequired_score,report_safe,rewrite_headeranduse_bayes, and verify withspamassassin --lintafter each change. For allow lists, use the 4.0-serieswelcomelist_fromfamily. - Update rules with
sa-update, and provide a local caching DNS resolver for the DNS blocklists. - Run Bayesian training against the database of the user that does the scoring, with at least 200 messages of each kind.
- Verify operation with GTUBE and the
X-Spam-Statusheader of received mail.
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)