Email infrastructure made right. Part 2: handling bounces.

In our previous post on email infrastructure we were talking about minimum requirements which should be implemented. Today we will cover bounces handling. What do the bounces mean? In short words, it’s a non delivery report. You could get if you send a mail to non-existing user (error code 5.1.1 ) or a user which has full mailbox (5.2.2).

There are kinds of the bounces:

  • soft bounces;
  • hard bounces.

If you get soft bounces it means that you can try to send an email later. And hard bounces mean that email account you are trying to send to is invalid. Such email accounts should not be used for further mail sending. And if you have a lot of subscribers you need a way to get rid of invalid email accounts. But how can you do it? Fortunately there’s a simple way. Probably you’ve heard about VERP. It stands for Variable envelope return path. Here’s how it works (we will cover Sendmail and Dovecot based setup on Centos).

Let’s assume we want to send a mail to from If there’s no user we will get a bounce message to But why don’t we collect all bounces in one mailbox? For simplicity of handling bounces we can setup some mail account. Let’s called it and setup our LDA to deliver all mails to some user account (it could be system user bounces or some other account depending on your setup). Set up Sendmail to forward all incoming mail sent to to system account bounces. Open /etc/aliases files and add next line: bounces

Tell Sendmail to use Dovecot as LDA:

FEATURE(`local_procmail', `/usr/libexec/dovecot/deliver',`/usr/libexec/dovecot/deliver -d $u')

It should be placed to /etc/mail/

Apply Sendmail changes:

cd /etc/mail ; make all restart

If you don’t have Dovecot installed it’s time to install it:

yum install dovecot

Here’s an example of Dovecot configuration file working as LDA:

# 2.0.9: /etc/dovecot/dovecot.conf
# OS: Linux 2.6.32-279.1.1.el6.x86_64 x86_64 CentOS release 6.3 (Final)
disable_plaintext_auth = no
lda_mailbox_autocreate = yes
listen =
mail_location = maildir:~/Maildir
mbox_write_locks = fcntl
passdb {
driver = pam
postmaster_address =
ssl = no
userdb {
driver = passwd
protocol lda {
auth_socket_path = /var/run/dovecot/auth-master
info_log_path = /var/log/dovecot/delivery-info.log
log_path = /var/log/dovecot/delivery.log
postmaster_address =

(Make sure you have /var/log/dovecot and dovecot has the permissions to write there).

Well, since we prepared everything let’s get back to VERP. To get a bounces while sending a mail to to bounces user maildir we need to add Return-Path header to our mail. Short example in PHP:

mail("", "Message subject", "Message body", "From:\n", "");

So, there will be additional header in the email:


In this case if there’s no user we will get a bounce message in our POP3/IMAP4 account bounces from where the messages could be easily fetched with simple Python script.

import imaplib
M = imaplib.IMAP4('')
M.login('bounces', 'strongest_password')
typ, data =, 'ALL')
for num in data[0].split():
typ, data = M.fetch(num, '(RFC822)')
print 'Message %s\n%s\n' % (num, data[0][1])

Once you get the messages you can parse it to get a list of invalid email accounts and make a modification of your users table in the database.

Leave a Reply