Postscreen is a brilliant piece of software that acts somewhat like a bouncer at the entrance of a nightclub; It helps keeping the trash outside and making sure the processes 'behind the door' have more time and resources to do what they need to do.

My experience is that implementing Postscreen helps blocking above 95% (!) of your typical spam e-mail before leaving the rest to Amavis.

Could be closer to 99%, come to think of it...

It's part of Postfix (>2.8), so no need for additional software.
More and way more detailed info at the Postfix site

postfix

For the people -like me- with little time on their hands or a typical form of sysadmin laziness about them, below the edits and additions needed to enable postscreen in a pretty default Postfix installation. Go ahead, copy-paste. Watch out for wrongly placed newlines though!

Addition to /etc/postfix/main.cf

content_filter = smtp-amavis:[127.0.0.1]:10024

#DNSBL
postscreen_dnsbl_threshold = 3
postscreen_dnsbl_sites =
  zen.spamhaus.org*3
  bl.mailspike.net*3
  b.barracudacentral.org*2
  bl.spameatingmonkey.net
  bl.spamcop.net
  spamtrap.trblspam.com
  dnsbl.sorbs.net=127.0.0.[2;3;6;7;10]
  ix.dnsbl.manitu.net
  bl.blocklist.de
  #whitelist
  list.dnswl.org=127.0.[0..255].0*-1
  list.dnswl.org=127.0.[0..255].1*-2
  list.dnswl.org=127.0.[0..255].[2..3]*-3
  iadb.isipp.com=127.0.[0..255].[0..255]*-2
  iadb.isipp.com=127.3.100.[6..200]*-2
  wl.mailspike.net=127.0.0.[17;18]*-1
  wl.mailspike.net=127.0.0.[19;20]*-2
postscreen_dnsbl_reply_map = texthash:/etc/postfix/dnsbl_reply
postscreen_dnsbl_action = enforce
postscreen_dnsbl_ttl = 1h

# more postscreen
postscreen_bare_newline_action = ignore
postscreen_bare_newline_enable = no
postscreen_bare_newline_ttl = 30d
postscreen_blacklist_action = enforce
postscreen_cache_cleanup_interval = 12h
postscreen_cache_map = btree:$data_directory/postscreen_cache
postscreen_cache_retention_time = 7d
postscreen_client_connection_count_limit = $smtpd_client_connection_count_limit
postscreen_command_count_limit = 20
postscreen_command_filter =
postscreen_command_time_limit = ${stress?10}${stress:300}s
postscreen_disable_vrfy_command = $disable_vrfy_command
postscreen_discard_ehlo_keyword_address_maps = $smtpd_discard_ehlo_keyword_address_maps
postscreen_discard_ehlo_keywords = $smtpd_discard_ehlo_keywords

#And the same for TLS:

postscreen_enforce_tls = $smtpd_enforce_tls
postscreen_use_tls = $smtpd_use_tls

#Some more settings; most of them are postscreen´s defaults:

postscreen_expansion_filter = $smtpd_expansion_filter
postscreen_forbidden_commands = $smtpd_forbidden_commands
postscreen_greet_action = enforce
postscreen_greet_banner = $smtpd_banner
postscreen_greet_ttl = 1d
postscreen_greet_wait = ${stress?2}${stress:6}s
postscreen_helo_required = $smtpd_helo_required
postscreen_non_smtp_command_action = drop
postscreen_non_smtp_command_enable = no
postscreen_non_smtp_command_ttl = 30d
postscreen_pipelining_action = enforce
postscreen_pipelining_enable = no
postscreen_pipelining_ttl = 30d
postscreen_post_queue_limit = $default_process_limit
postscreen_pre_queue_limit = $default_process_limit
postscreen_reject_footer = $smtpd_reject_footer
postscreen_tls_security_level = $smtpd_tls_security_level
postscreen_watchdog_timeout = 10s

Change/Addition to /etc/postfix/master.cf

smtpd     pass  -       -       n       -       -       smtpd
smtp      inet  n       -       n       -       1       postscreen
dnsblog   unix  -       -       n       -       0       dnsblog
tlsproxy  unix  -       -       n       -       0       tlsproxy
submission inet n       -       n       -       -       smtpd

And:

smtp-amavis     unix -        -       y     -       8  smtp
  -o smtp_data_done_timeout=1200
  -o smtp_send_xforward_command=yes
  -o disable_dns_lookups=yes

127.0.0.1:10025 inet n        -       n     -       -  smtpd
    -o content_filter=
    -o disable_dns_lookups=yes
    -o smtpd_restriction_classes=
    -o smtpd_client_restrictions=
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o mynetworks=127.0.0.0/8
    -o mynetworks_style=host
    -o strict_rfc821_envelopes=yes

/etc/postfix/dnsbl_reply

A mapping from actual DNSBL domain name which includes a secret password, to the DNSBL domain name that postscreen will reply with when it rejects mail. When no mapping is found, the actual DNSBL domain will be used.

# Secret DNSBL name Name in postscreen(8) replies
secret.zen.spamhaus.org zen.spamhaus.org


DNS Edits


zone "multi.uribl.com" { 
     type forward; 
     forward first; 
     forwarders {}; 
};

zone "dnsbl.sorbs.net" { 
     type forward; 
     forward first; 
     forwarders {}; 
};

zone "combined.njabl.org" { 
     type forward; 
     forward first; 
     forwarders {}; 
};

zone "activationcode.r.mail-abuse.com" { 
     type forward; 
     forward first; 
     forwarders {}; 
};

zone "nonconfirm.mail-abuse.com" { 
     type forward; 
     forward first; 
     forwarders {}; 
};

zone "iadb.isipp.com" { 
     type forward; 
     forward first; 
     forwarders {}; 
};

zone "bl.spamcop.net" { 
     type forward; 
     forward first; 
     forwarders {}; 
};

zone "fulldom.rfc-ignorant.org" { 
     type forward; 
     forward first; 
     forwarders {}; 
};

zone "list.dnswl.org" { 
     type forward; 
     forward first; 
     forwarders {}; 
};

zone "blackholes.mail-abuse.org" { 
     type forward; 
     forward first; 
     forwarders {}; 
};

zone "bl.score.senderscore.com" { 
     type forward; 
     forward first; 
     forwarders {}; 
};

zone "zen.spamhaus.org" { 
     type forward; 
     forward first; 
     forwarders {}; 
};

Test with:

host 2.0.0.127.list.dnswl.org

If everything works it should return:

2.0.0.127.list.dnswl.org has address 127.0.10.0


Strange log entries about a missing database?

On Debian based systems i ran into the issue of log entries similar to this one:

postfix/postscreen[9697]: 
close database /var/lib/postfix/postscreen_cache.db: 
No such file or directory (possible Berkeley DB bug)

This is because postfix/postscreen are running in a chroot jail by default on these systems. However, the 'missing' files seem to be outside the jail, so the process can't find them. To workaround this 'issue', we're going to place the file inside the jail and create a symlink to it's original location:

systemctl stop postfix
cd /var/lib/postfix
mkdir -p /var/spool/postfix/var/lib/postfix
mv postscreen_cache.db /var/spool/postfix/var/lib/postfix
ln -s /var/spool/postfix/var/lib/postfix/postscreen_cache.db
systemctl start postfix