We have a postfix installation with a couple of virtual domains each with virtual users. These domains and users are mapped using a mysql database. I have been until now tracking bounces by parsing the postfix log file. I suspect there must be better and more efficient ways of doing this. I thought of three but I am not sure what is best:

  1. Write a Postfix content filter that logs the bounce and throws away the mail
  2. Use procmail - but I am not sure how procmail would work with virtual users who have no $HOME defined
  3. Write a script that POPs mail from mailboxes; parses and logs them and deletes the bounced email

I would appreciate advise on which would be best from a maintenance point of view and efficient from conserving server resources point of view. Thanks


This all assumes that you want to collect info on bounces rather than the bounced email itself:

I have pretty much the same setup with postfix, mysql, and virtual hosts. From n hardware resource perspective the most efficient way to keep track of this is to parse the log files as you are doing. But if you think how you're parsing is to big of a load, you can use an application like Logwatch to do all the parsing for you. Then set up Postfix to ditch the bounced files for you.

Now if you decide you actually want to collect these emails somewhere you can use these settings in the main.cf file:

bounce_notice_recipient = someone@nowhere.com
error_notice_recipient = someone@nowhere.com

And if you want the emails to be completely destroyed you can add a virtual user and adjust you aliases file to send them to dev/null

someone: /dev/null

As for a script and a database I work with PHP and MySQL a lot these days so if I were to use those tools I could create some php code to read in the log file, look for the bounces, and then right them into a database. Then I'd run the code before the mail.log was truncated. In fact, I'll post the code here after I write it for kicks.

Here's some code if you want to run this with php/mysql (I'm sure it could be prettier):

# load local file into array
$val = file("mail.log");

$pattern = '/status=bounced/';

foreach ($val as &$value) {
if (preg_match($pattern,$value)) {
        $a = split('[<>]', $value);

       //if you prefer you can also use: preg_match_all('/<(.*)>/', '$value', $matches);
       #can be helpful to print the following to the screen during tests
       # echo $a[1];

        // Make a MySQL Connection
        mysql_connect("localhost", "username", "password") or die(mysql_error());
        mysql_select_db("postfix_db") or die(mysql_error());

        // Insert a row of information into the table "example"
        mysql_query("INSERT INTO emails (emailaddress) VALUES('$a[1]') ") 
        or die(mysql_error());  

        #again, if you want to see while running manually from cli
        #echo "Data Inserted!";

#again, if you want to see while running manually from cli
#echo "\n";


You could then fire off a cron right before your mail.log is set to be recycled or clear out the log once the cron fires off.

Seems like a lot of effort to keep track of email addresses of bounced emails. You'll need to write mysql queries to then access this info of course.

You could also skip the mysql stuff completely and just pipe the results to a test file or an email address (and could also use a cron)

php parse.php > results.txt


php parse_logs.php | /usr/sbin/sendmail someemailaddress@nowhere.com
  • 2
    Voted this down. Using file() on a mail log is generally a bad idea. A server that sends any volume of mail will have logs that will amass tens if not hundreds of megabytes fairly quickly. file() loads the entire file at once into an array [memory]. Additionally there are some documented issues with this function pertaining to accessing files that other processes are accessing, which will be the case with a mail log. – Kyle Buser Feb 17 '11 at 21:28

I don't have an example, as I've never tried to do it, but you could use syslog-ng (depending on your platform), and create a filter. filter has an option to perform a regex match on the message itself. All you'd need to do is send it to a specific destination, that destination would be mysql. A quick search will tell you how to setup syslog-ng to mysql, and some tweaking will probably get you a filter to send to that destination.


You can pipe bounce notifications to a script. Just as an overview how you can implement it:


notify_classes = bounce, 2bounce, resource, software


bouncepipe@example.com     bouncepipe:


bouncepipe   unix  -       n       n       -       -       pipe
      flags=DRhu user=list argv=/etc/postfix/bouncepipe.pl



my $message = '';
my $sender = '';
my $recipient = '';

foreach $line ( <STDIN> )
    $message .= $line;
    chomp( $line );
    if ( $line =~ /Final-Recipient: /)
        my $index = index($line, ';');
        $recipient = substr($line, $index+2);
    if ( $line =~ /X-Postfix-Sender: /)
        my $index = index($line, ';');
        $sender = substr($line, $index+2);
# Do whatever you need to do with $sender and $recipient

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.