[Postfix] pipe email to your script via transport table


Postfix Concept

Components :

  • Programs/Components
    • sendmail: this command is compatible with sendmail of Sendmail , it is used to deliver e-mail from local.
    • postdrop: this command is designed to run with set-group ID privileges, so that it can write to the maildrop queue directory and so that it can connect to Postfix daemon processes.
    • smtpd: handle SMTP/ESMTP Client requests
    • pickup: handle emails from local queue (maildrop)
    • cleanup:
      1. normalize mail format
      2. put mail to incoming queue
    • smtp: SMTP Client
    • local: deliver e-mail to local user
    • pipe: put email to external program
  • Queues  ( Postfix manual – qmgr(8) )
    • maildrop: used to queue local emails from sendmail/postdrop
    • incoming: Inbound mail from the network, or mail picked up by the local pickup(8) daemon from the maildrop directory.
    • active: Messages that the queue manager(qmgr) has opened for delivery. Only a limited number of messages is allowed to enter the active queue (leaky bucket strategy, for a fixed delivery rate).
    • deferred: Mail that could not be delivered upon the first attempt. The queue manager implements exponential backoff by doubling the time between delivery attempts.
  • Transport Table
    • The optional transport table specifies a mapping from email addresses to message delivery transports and next-hop destinations.

Postfix server settings :

  • /etc/postfix/master.cf ( Postfix manual – master(5) )
    • Maximum Sonic API QPS can be configured in here ( Process limit , default is 100 )
      Process limit (default: $default_process_limit) The maximum number of processes that may execute this service simultaneously. Specify 0 for no process count limit.
  • /etc/postfix/main.cf
    • initial_destination_concurrency and destination_concurrency_limit Postfix Performance Tuning
    • maximal_queue_lifetime ( default is 5 days ) : Consider a message as undeliverable, when delivery fails with a temporary error, and the time in the queue has reached the maximal_queue_lifetime limit.
    • For debug :
      • dont_remove:  Don’t remove queue files and save them to the “saved” mail queue. This is a debugging aid. To inspect the envelope information and content of a Postfix queue file, use the postcat(1) command.
      • smtpd -v -D <= defined in /etc/postfix/master.cf
  • Postfix Queues are as directory and put under /var/spool/postfix

Some useful commands :

Postfix – Use Transport Table to well routed e-mail to right destination

1. ready your script

Example :


script name : /home/y/bin/mail_relay.php

component name : smtp_to_script

2. add your component to /etc/postfix/master.cf ( component name can be defined by yourself )

$ cat /etc/postfix/master.cf | head -n 1

smtp_to_script unix - n n - - pipe flags=Rq user=nobody argv=/bin/mail_relay.php ${sender} ${recipient}


  • mail content is passed by PIPE
  • sender and recipients are passed by arguments
  • The meaning of return codes of Postfix : ( ref: Postfix After-Queue Content Filter  )
    • 0 : OK, Postfix will think the email is delivered successfully ( no any further action required )
    • 69: If the content filter program finds a problem, the mail is bounced by terminating with exit status 69 (EX_UNAVAILABLE). Postfix will send the message back to the sender as undeliverable mail.
    • 75: If the message cannot be captured to file, mail delivery is deferred by terminating with exit status 75 (EX_TEMPFAIL). Postfix places the message in the deferred mail queue and tries again later.

3. ensure your postfix will reference transport table

Default, postfix delivers local user email to local: component and external email to relayhost (smtp)

If you want to customize your mail routing, please ensure your postfix has below setting :

$ cat /etc/postfix/main.cf | grep transport

transport_maps = regexp:/etc/postfix/transport

Once you modify postfix configurations, please remember to execute below command to effect settings :

$ sudo postfix reload


$ sudo service postfix restart


4. define your mail routing in transport table ( what kind of emails will be pipe to your script ? )

syntax reference : Postfix manual – regexp_table(5)  , the match rule of table lookup is first match

$ cat /etc/postfix/transport

/corp.dozstyle.io/ smtp:mail.dozstyle.io
/(localhost|api.dozstyle.io)/ local:
!/(localhost|api.dozstyle.io)/ smtp_to_script:dummy

Rule explain :

  • email to corp.dozstyle.io domain will be relay to mail.dozstyle.io
  • email to localhost or self-hostname will be delivered to local user
  • otherwise, email will be pipe to external script

Once you modify transport mapping, please remember to execute below command to update transport database :

 $ sudo postmap /etc/postfix/transport

How to verify your transport table ?

$ postmap -q chenlego@corp.dozstyle.io regexp:/etc/postfix/transport

$ postmap -q chenlego@api.dozstyle.io regexp:/etc/postfix/transport

$ postmap -q chenlego@gmail.com regexp:/etc/postfix/transport

5. Test result

Test command

echo "mail test" | mail -s "email test to different routing target" chenlego@corp.dozstyle.io,chenlego@api.dozstyle.io,chenlego@gmail.com

Result : /var/logs/maillog

Sep 02 08:01:48 api.dozstyle.io postix/pickup[24983]: 49C0187352: uid=1111 from=<chenlego>
Sep 02 08:01:48 api.dozstyle.io postix/cleanup[28817]: 49C0187352: message-id=<2017042080148.49C0187352@api.dozstyle.io>
Sep 02 08:01:48 api.dozstyle.io postix/qmgr[8366]: 49C0187352: from=<chenlego@api.dozstyle.io>, size=578, nrcpt=3 (queue active)

Sep 02 08:01:48 api.dozstyle.io postix/local[28821]: 49C0187352: to=<chenlego@api.dozstyle.io>, orig_to=<chenlego>, relay=local, delay=0.06, delays=0.03/0.01/0/0.03, dsn=2.0.0, status=sent (deliverd to mailbox)

Sep 02 08:01:49 api.dozstyle.io postix/smtp[28819]: 49C0187352: to=<chenlego@corp.dozstyle.io>, relay=mail.dozstyle.io[]:25, delay=0.96, delays=0.03/0.01/0.66/0.27, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 0E29D5C01A4)

Sep 02 08:01:49 api.dozstyle.io postix/pipe[28820]: 49C0187352: to=<chenlego@gmail.com>, relay=smtp_to_script, delay=1, delays=0.03/0.01/0/1, dsn=2.0.0, status=sent (delivered via smtp_to_script service)

Sep 02 08:01:49 api.dozstyle.io postix/pipe[8366]: 49C0187352: removed


Postfix After-Queue Content Filter

Triggering a PHP script when you Postfix server receives a mail

[Python] Jupyter Server Setup

Purpose: I’d like to setup a jupyter sever that only can be edited by me and I also can share the jupyter notebooks to anyone. There are two URLs, one for admin (me) and another for share to anyone. Admin: https://jupyter-admin.chenlego.me Share(guest): https://jupyter.chenlego.me But, current jupyter server doesn’t support permission management for …

Server stuff
[SSL] Let’s encrypt How to

https://letsencrypt.org/ I only record command steps. Install pre-required packages $ sudo yum install gcc libffi-devel python-devel openssl-devel git Get letsencrypt-auto script from GitHub repo letsencrypt git link $ sudo git clone https://github.com/letsencrypt/letsencrypt /usr/share/letsencrypt Cloning into 'letsencrypt'… remote: Counting objects: 48173, done. remote: Compressing objects: 100% (14/14), done. remote: Total 48173 (delta …

Server stuff
How to verify which SSL/TLS Protocols are supported on the FTP Server

Today, I disabled SSLv2, SSLv3, TLSv1.0 and TLSv1.1 SSL/TLS protocols and remain TLSv1.2 only on my ProFTPd FTP Server. The configurations were configured but I still not sure if my settings are workable? So, I surveyed two ways to do that. 1. Use Python ftplib ( Preferable ) I prefer …