Using postfix, I'd like all incoming mail, to any address (including those that don't map to local users) to be piped to a script. I've tried configuring mailbox_command in /etc/postfix/main.cf:

mailbox_command = /path/to/myscript.py

This works great if the user is a local user, but it fails for "unknown" users who don't have aliases. I tried setting luser_relay to a local user, but this pre-empts mailbox_command, and so the command doesn't get run. I tried setting local_recipient_maps= (empty string), but the message is still bounced (unknown user).

Is there a magic invocation I can use to get all known and unknown users to go to the script as well?

Full /etc/postfix/main.cf follows -- it's the default Ubuntu 10.04, with the exception of the mailbox_command line:

# See /usr/share/postfix/main.cf.dist for a commented, more complete version

# Debian specific:  Specifying a file name will cause the first
# line of that file to be used as the name.  The Debian default
# is /etc/mailname.
#myorigin = /etc/mailname

smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu)
biff = no

# appending .domain is the MUA's job.
append_dot_mydomain = no

# Uncomment the next line to generate "delayed mail" warnings
#delay_warning_time = 4h

readme_directory = no

# TLS parameters
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
# information on enabling SSL in the smtp client.

myhostname = ... snip ...
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
mydestination = sassafras, ... snip ...,localhost.localdomain, localhost
relayhost =
mynetworks = [::ffff:]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all

mailbox_command = /path/to/my/script.py
  • After the script, what then? This sounds like the kind of thing that virus scanners and spam filters do, maybe you can find a guide to configuring one of those and use its configuration for your script?
    – DerfK
    Apr 11, 2011 at 20:48
  • The messages go to a web application. So after the script: a database, other processing, other stuff, but not normal mail stuff. The key question is getting the unknown users to not bounce -- this is outside the normal virus/spam scenario, where you would bounce unmapped users.
    – user67641
    Apr 11, 2011 at 20:53
  • @user67641 hi do i need to inject the mail into postfix in my script or by adding some more lines in master.cf
    – merveotesi
    Dec 4, 2013 at 8:40

4 Answers 4


Ok, I just got this working -- though hairier than I thought it would be. I dropped the maildir_command part, and went with transport_maps. The key is to do 5 things:

  1. Set up a db file to handle aliases (and add a catch-all alias)
  2. Set up a db file to map the 'transport' for the domain in question to a special handler.
  3. Compile the db files into berkeley db format that postfix wants.
  4. Set up the handler in /etc/postfix/master.cf to pipe mail to the script.
  5. Set /etc/postfix/main.cf to use the transport db for transport_maps, and the alias db for virtual_alias-maps.

(1) Create /etc/postfix/virtual_aliases to add a catch-all alias -- localuser needs to be an existing local user:

@mydomain.tld   localuser@mydomain.tld

(2) Create /etc/postfix/transport to add a transport mapping. "mytransportname" can be whatever you want; it's used below in master.cf:

mydomain.tld    mytransportname:

(3) Next, both transport and virtual_aliases need to be compiled into berkeley db files:

$ sudo postmap /etc/postfix/virtual_aliases
$ sudo postmap /etc/postfix/transport

(4) Add the transport to /etc/postfix/master.cf:

mytransportname   unix  -       n       n       -       -       pipe
  flags=FR user=localuser argv=/path/to/my/script.py
  ${nexthop} ${user}

(5) In /etc/postfix/main.cf:

  transport_maps = hash:/etc/postfix/transport
  virtual_alias_maps = hash:/etc/postfix/virtual_aliases

And... good to go! Sheesh.

  • 1
    extremely useful answer
    – merveotesi
    Nov 12, 2013 at 15:42
  • 1
    Virtual alias maps are not needed. You can accomplish the same thing with relay_domains (in which case the transport file can filter based on the actual To: address).
    – Brilliand
    Jul 14, 2015 at 21:03
  • I found you can route all virtual mail to a script with virtual_transport = mailcaptcha: in main.cf and mailcaptcha unix - n n - - pipe flags=FR user=vmail argv=/usr/bin/perl /usr/local/mail-captcha/verify.pl ${sender} ${recipient} in master.cf. See postfix.org/transport.5.html and postfix.org/VIRTUAL_README.html.
    – Chloe
    Jan 19, 2017 at 18:44
  • The (4) should be in one single line, is that right? Also doesn't it require a postfix restart or reload or something like this? Last thing: is there a log file to see the mails who arrived to postfix?
    – Basj
    Nov 21, 2017 at 15:25
  • @Basj, the log file is /var/log/mail.log.
    – fiedl
    Jul 22, 2019 at 19:18

The only time I've used something like this was for a specific user's mailbox. All that was required was to alias that user's name to a pipe and a process in aliases:

pong: "| /usr/local/bin/gotit.pl"

This sent traffic destined for "pong@mymailserver.com" to a perl script I wrote to process it.

gotit.pl (as an example, don't pick on me for crappy programming skillz =). It's job was to process an email I'd sent to our Exchange server (where it was auto-replied via some VB code) to verify that Exchange was processing email in a timely fashion. If not, the mail server would send out an alert email to our pagers and write a lock file so we didn't get constantly spammed.

#! /usr/bin/perl -w
use vars qw ( $mung $sent $tvalue $remainder $delta $fout );
$mung = time;
while (<STDIN>) {
    ($sent, $tvalue, $remainder ) = split /: /, $_, 3;
    $tvalue =~ s/(\D+)//g;
    $delta = $mung-$tvalue;
    if ( $sent =~ "Sent" ) {
        $fout = "/var/spool/mailcheck/$tvalue";
        next unless ( -e $fout );
        open (TMP, "> $fout") or die "Couldn't open output file: $!\n";
        print TMP "Received in: $delta seconds.\n";
                close TMP;
  • Yeah, and just redirect @mydomain.tld to pong and all incoming main for mydomain.tld will be piped.
    – Zaar Hai
    Jul 13, 2014 at 10:55
  • and for those of us using multiple virtual domains, don't forget to redirect it to pong@localhost so that it doesn't attach the default domain name to the alias Aug 22, 2019 at 22:25

After a lot of headaches I put together this solution based on a couple of different sources that resulted in much less effort, the critical steps were configuring virtual_alias_domains as well as virtual_alias_maps and making sure that the virtual mapping was to my-alias@localhost instead of just my-alias. In my example the command alias is to pipe the email to a website API endpoint, but it could just as easily pipe into something else.

Here are the steps you'll need to take:

  • Set up your A and MX records for your domain, the A record @ pointing to the IP of the server you’re going to be receiving emails on and MX with the hostname @ and the value 10 mail.your-domain-name
  • sudo apt-get install postfix
  • Select "Internet Site" and enter your-domain-name (fully qualified)
  • sudo vi /etc/postfix/main.cf
  • Add mail.your-domain-name to the list of mydestination values
  • Append
virtual_alias_domains = hash:/etc/postfix/virtual_domains
virtual_alias_maps = hash:/etc/postfix/virtual

to the end of the file

  • sudo apt-get vi /etc/aliases
curl_email: "|curl --data-binary @- http://your-domain-name/email"
  • sudo newaliases
  • sudo apt-get vi /etc/postfix/virtual_domains
example.net   #domain
example.com   #domain
your-domain-name   #domain

(the #domain fields suppress warnings)

  • sudo postmap /etc/postfix/virtual_domains
  • sudo apt-get vi /etc/postfix/virtual
info@your-domain-name bob@gmail.com
everyone@your-domain-name bob@gmail.com jim@gmail.com
email_processor@your-domain-name curl_email@localhost
@your-domain-name catchall@whereveryouwant.com
ted@example.net jane@outlook.com
  • sudo postmap /etc/postfix/virtual
  • sudo /etc/init.d/postfix reload

I've used an old style "flat file" Mailbox to receive all mail (and then prune it every few hours if it's large), instead of using the modern maildir/ folders, to process mail through scripts. You could run logrotate over the file too I suppose to keep it manageable.

This way you could simply copy all mail to a Mailbox as a local user.

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.