Use Postfix Transport Map & Relayhost Map For Flexible Email Delivery

In previous tutorials, we discussed how to quickly set up a full-featured mail server using iRedMail or Modoboa, and we also learned how to set up SMTP relay with Postfix SMTP server to bypass port 25 blocking or IP blacklists. However, you might not want to set up your entire email server to use a relay host. We can configure Postfix transport map and relay map so that some emails are delivered via relay host, other emails are sent directly to recipients.

There are many SMTP relay services. In this tutorial, I use Sendinblue which allows you to send 9000 emails per month for free. SMTP relay services maintain a good IP reputation. They stop the bad senders and grow the good ones, so using SMTP relay service will increase the chance of hitting the inbox.

Use Postfix Transport Map and Relay Map For Flexible Email Delivery

How to Use Postfix Transport Map

The transport map defines mappings from recipient address to transport method. By default, the value of the transport_maps parameter in Postfix is not set, as can be checked with:

postconf transport_maps

Sample output:

transport_maps =

iRedMail and Modoboa use MySQL/MariaDB database to store transport maps. If you used iRedMail to set up your mail server, then the output should be like:

transport_maps = proxy:mysql:/etc/postfix/mysql/transport_maps_user.cf proxy:mysql:/etc/postfix/mysql/transport_maps_domain.cf

If you used Modoboa to set up your mail server, then the output should be like:

transport_maps = proxy:mysql:/etc/postfix/sql-transport.cf proxy:mysql:/etc/postfix/sql-spliteddomains-transport.cf

The transport_maps parameter points to one or more transport lookup tables. You can edit the Postfix main configuration file.

sudo nano /etc/postfix/main.cf

And set a value by adding the following line at the end of the file. The /etc/postfix/transport file will contain the lookup table.

transport_maps = hash:/etc/postfix/transport

If you use iRedMail, you can find the transport_maps parameter and set the value to

transport_maps =
    hash:/etc/postfix/transport
    proxy:mysql:/etc/postfix/mysql/transport_maps_user.cf
    proxy:mysql:/etc/postfix/mysql/transport_maps_domain.cf

If you use Modoboa, you can find the tranport_maps parameter and set the value to:

transport_maps =
        hash:/etc/postfix/transport
        proxy:mysql:/etc/postfix/sql-transport.cf
        proxy:mysql:/etc/postfix/sql-spliteddomains-transport.cf

A lookup table can be a file, or in the form of MySQL/MariaDB database tables. Lookup tables will be searched in the specified order until a match is found.

Save and close the Postfix main configuration file. Next, we need to create the lookup table file.

sudo nano /etc/postfix/transport

In this file, we can define mappings from recipient addresses to transport methods. For example, I found that many .pl (Poland) domains are using a particular blacklist that blocks my mail server’s IP address. I can add the following line in this file so that emails sent to .pl domains will be relayed through Sendinblue.

.pl                relay:[smtp-relay.sendinblue.com]:587

Some people find that it’s hard to get into the inbox of Microsoft mailboxes (hotmail.com, outlook.com, etc). It’s very likely that your email will be put in the spam folder. Well, you can try using Sendinblue to deliver emails to Microsoft mailbox users. Sendinblue even allows you to see if the recipient opened or clicked links in your email. So I put the following lines in the file.

/.*@hotmail.*/i             relay:[smtp-relay.sendinblue.com]:587
/.*@outlook.*/i             relay:[smtp-relay.sendinblue.com]:587
/.*@live.*/i                relay:[smtp-relay.sendinblue.com]:587
/.*@msn.*/i                 relay:[smtp-relay.sendinblue.com]:587

If you want to use relay host to deliver emails to a particular recipient, but send emails directly to all other recipients in the same domain, then you can add a line like below.

[email protected]           relay:[smtp-relay.sendinblue.com]:587

If a certain SMTP server doesn’t use the default SMTP port 25, but uses a different port such as 2525 to receive incoming emails, you can add the following line

example.com        smtp:[mail.example.com]:2525

It’s a good practice to add your own domain name in this file like below.

your-domain.com    local

This tells Postfix that emails sent to your own domain should be delivered locally. This is the default behavior for canonical domains. If your mail server has multiple virtual domains, you should add all of your virtual domains.

your-domain1.com         local
your-domain2.com         local

If you just put the following two lines in the file and don’t add other lines, this will make all emails, excluding emails sent to your own domain, delivered via the relay host. The asterisk (*) is a wild-card character that represents any email address.

your-domain.com       local
*                     relay:[smtp-relay.sendinblue.com]:587

We can also have the following configuration, which means that emails sent to your own domain are delivered locally. Email sent to gmail.com are delivered normally by performing MX lookup and all other emails are delivered via the relay host.

your-domain.com       local 
gmail.com             smtp
*                     relay:[smtp-relay.sendinblue.com]:587

Save and close the file. Then run the following command to build the index file.

sudo postmap /etc/postfix/transport

Restart Postfix for the changes to take effect.

sudo systemctl restart postfix

Sender Dependent Relay Maps

The transport map defines mappings from a recipient address to transport method. If you want to define mappings from sender address to relay hosts, use the sender_dependent_relay_maps parameter. By default, its value is empty, as can be seen with:

postconf sender_dependent_relayhost_maps

Output:

sender_dependent_relayhost_maps =

iRedMail uses MySQL/MariaDB database to store sender-dependent relayhost maps. If you used iRedMail to set up your mail server, then the output should be like:

sender_dependent_relayhost_maps = proxy:mysql:/etc/postfix/mysql/sender_dependent_relayhost_maps.cf

The sender_dependent_relayhost_maps parameter points to one or more lookup tables. You can edit the Postfix main configuration file.

sudo nano /etc/postfix/main.cf

And set a value by adding the following line at the end of the file. The /etc/postfix/relay_by_sender file will contain the lookup table.

sender_dependent_relayhost_maps = hash:/etc/postfix/relay_by_sender

If you use iRedMail, you can find the sender_dependent_relayhost_maps parameter and set the value to

sender_dependent_relayhost_maps =
    hash:/etc/postfix/relay_by_sender
    proxy:mysql:/etc/postfix/mysql/sender_dependent_relayhost_maps.cf

Lookup table can be a file, or in the form of MySQL/MariaDB database tables. Lookup tables will be searched in the specified order until a match is found.

Save and close the Postfix main configuration file. Next, we need to create the lookup table file.

sudo nano /etc/postfix/relay_by_sender

Add rules like below, this will make emails sent from [email protected] delivered via the relay host specified on the right side.

[email protected]           [smtp-relay.sendinblue.com]:587

Let’s say if you have a Linux server that hosts two websites and each website has its own mail server running on two separate hosts, then you can add the following two lines to make each website uses its own mail server.

@domain1.com                     [mail.domain1.com]:587
@domain2.com                     [mail.domain2.com]:587

Save and close the file. Then build the index file.

sudo postmap /etc/postfix/relay_by_sender

Restart Postfix for the changes to take effect.

sudo systemctl restart postfix

Set Up SMTP Authentication

Now we need to set up SMTP authentication so that the Postfix SMTP client can use the relay host. Edit the Postfix main configuration file.

sudo nano /etc/postfix/main.cf

Add the following lines at the end of this file. The /etc/postfix/sasl_password will contain the username and password.

# outbound relay configurations
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = may
header_size_limit = 4096000

If you have set the relayhost parameter, then I recommend giving it an empty value like below, because we are now using transport maps and sender dependent relayhost maps.

relayhost =

Save and close the file. Then create the /etc/postfix/sasl_passwd file.

sudo nano /etc/postfix/sasl_passwd

Add the SMTP relay host and SMTP credentials to this file like below. Replace smtp_username and smtp_password with your own username and password that are given by SendinBlue. Note there’s a colon between the username and password.

[smtp-relay.sendinblue.com]:587            smtp_username:smtp_password

Save and close the file. Then create the corresponding hash db file with postmap.

sudo postmap /etc/postfix/sasl_passwd

Now you should have a file /etc/postfix/sasl_passwd.db. Restart Postfix for the changes to take effect.

sudo systemctl restart postfix

By default, sasl_passwd and sasl_passwd.db file can be read by any user on the server.  Change the permission to 600 so only root can read and write to these two files.

sudo chmod 0600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db

Testing

Now you can send test email to the recipients defined in transport maps, or send an email from the address specified in sender dependent relayhost maps. Then check the mail log (/var/log/mail.log or /var/log/maillog) to see if it’s working.

Note that if you are using a third-party SMTP relay service like Sendinblue, then it’s likely that you are required to validate your domain name in your account and edit SPF and DKIM record.

Troubleshooting

error #1

If your email wasn’t delivered and you found the following message in the mail log (/var/log/maillog),

Relay access denied (in reply to RCPT TO command))

then you might need to edit the /etc/postfix/sasl_passwd file and remove the port number after the hostname like below.

smtp-relay.sendinblue.com    smtp_username:smtp_password

Save and close the file. Then build the index file again.

sudo postmap /etc/postfix/sasl_passwd

Restart Postfix for the changes to take effect.

sudo systemctl restart postfix

Now you can flush the email queue (attempt to deliver the previous emails).

sudo postqueue -f

error #2

Note that sender_dependent_relayhost_maps doesn’t support the special keywords: smtp, local, etc. If you see the following error in Postfix logs, it means you used the smtp keyword.

Host or domain name not found. Name service error for name=smtp type=A: Host not found

Bonus Tip: smtp_fallback_relay

You can specify a fallback relay host in Postfix. This way, if an SMTP destination can’t be reached (no MX record, no A record) or the primary relay host is offline, Postfix will use the fallback relay host.

Edit the main Postfix configuration file.

sudo nano /etc/postfix/main.cf

Add the following line in this file. Replace secondary.relayhost.com with the actual relayhost name. This relay host can be from Sendinblue or any other SMTP relay services.

smtp_fallback_relay = [secondary.relayhost.com]:587

Save and close the file. Then you should edit the /etc/postfix/sasl_passwd file and add the SMTP credentials for the fallback relay host just like above. Finally, restart Postfix for the changes to take effect.

Note that if the SMTP destination is using greylisting to temporarily reject email messages, then your Postfix SMTP server will also use the fallback relay host.

Per-User Relay Rules with Virtual Mailboxes

Let’s say you have set up business emails on a third-party service like Zoho. Now you have a self-hosted email server which is using virtual mailboxes. You want to keep the old email addresses on Zoho, but create new email addresses in your own email server. In this case, you need to create per-user relay rules so that emails destined for the old email addresses will be sent to the Zoho mail server.

Edit Postfix main configuration file.

sudo nano /etc/postfix/main.cf

Find the virtual_mailbox_maps parameter and add hash:/etc/postfix/transport to the value field.

virtual_mailbox_maps =
   hash:/etc/postfix/transport
   proxy:mysql:/etc/postfix/sql/mysql_virtual_mailbox_maps.cf,
   proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf

Save and close the file. Then in the /etc/postfix/transport file, you can define relay rules for the old email addresses like below.  (Note: You can also use the relay keyword instead of smtp. They are both correct in this case.)

[email protected]      smtp:[mx.zoho.com]:25
[email protected]      smtp:[mx.zoho.com]:25
[email protected]      smtp:[mx.zoho.com]:25

Save and close the file. Then build the hash table for the /etc/postfix/transport file.

sudo postmap /etc/postfix/transport

Then restart Postfix.

sudo systemctl restart postfix

Wrapping Up

I hope this tutorial helped you use Postfix transport maps and sender dependent relayhost maps. As always, if you found this post useful,  subscribe to our free newsletter to get more tips and tricks. Take care 🙂

Rate this tutorial
[Total: 9 Average: 4.9]

34 Responses to “Use Postfix Transport Map & Relayhost Map For Flexible Email Delivery

  • BangkokBob
    4 years ago

    This is a great tutorial! As usual! I have been waiting for this. I only have one question remaining, I use iRedmail. The transport maps are where expected. How do I edit the mysql database to redirect a domain. Is it done in the cf file or directly to the db? What is the format?
    Many thanks

    • Xiao Guoan (Admin)
      4 years ago

      iRedAdmin Pro users can easily manage transport maps in the admin panel. If you use the free version of iRedAdmin, you need to log into MySQL/MariaDB and use SQL command to add transport maps. More details can be found at https://docs.iredmail.org/per-account.transport.html

      You can still create transport maps on iRedMail server with the plain text file /etc/postfix/transport, if the parameter is set to:

      transport_maps =
              hash:/etc/postfix/transport
              proxy:mysql:/etc/postfix/sql-transport.cf
              proxy:mysql:/etc/postfix/sql-spliteddomains-transport.cf
      
  • Markus Fischer
    4 years ago

    Thanks for this tutorial… I use the sender_dependent_relayhost_maps and it works – but only for recipient domains, which are not local mail domains. This is the postfix standard and might be usefull in most cases.
    Is there an option to change this behaviour? To always use the SMTP relay insted of local delivery even if sender and recipient are on the same server?
    The use case is that the SMTP Relay Server manages archiving for example…

    • Xiao Guoan (Admin)
      3 years ago

      In /etc/postfix/transport file, add

      your-domain.com      relay:[smtp-relay.sendinblue.com]:587

      Then

      sudo postmap /etc/postfix/transport
      sudo systemctl restart postfix
      • Effendy
        3 years ago

        What if I want only a few domains using sendinblue and the rest goes to the server’s smtp. I have about 80 domains, looking for an easier way instead of listing them one by one.

  • RS BARI
    4 years ago
    sudo systemctl restart postfix
    

    My log:

    Jul 15 23:47:25 vultr postfix/postfix-script[54135]: stopping the Postfix mail system
    Jul 15 23:47:25 vultr postfix/master[54103]: terminating on signal 15
    Jul 15 23:47:28 vultr postfix/postfix-script[55314]: warning: symlink leaves directory: /etc/postfix/./makedefs.out
    Jul 15 23:47:28 vultr postfix/postfix-script[55484]: starting the Postfix mail system
    Jul 15 23:47:28 vultr postfix/master[55486]: warning: duplicate master.cf entry for service "smtps" (465) -- using the last entry
    Jul 15 23:47:28 vultr postfix/master[55486]: daemon started -- version 3.4.13, configuration /etc/postfix
    

    How yo fix it?

    • Xiao Guoan (Admin)
      4 years ago

      You have duplicate entries in /etc/postfix/master.cf file. You have two smtps services.

      • RS BARI
        4 years ago

        Ok tks XIao, what about warning on this line:

        Jul 15 23:47:28 vultr postfix/postfix-script[55314]: warning: symlink leaves directory: /etc/postfix/./makedefs.out
        
  • Alejandro
    4 years ago

    Very detailed tutorial!

  • Ryan Kh
    4 years ago

    Hi Xiao,
    Thanks for sharing such detailed and helpful tutorial. Would you mind helping me to setup the same configuration for Exim 4.9x? Actually, I’ve been using DirectAdmin control panel and this comes up with Exim MTA only. And my IP is whitelisted on all major mail servers except the Outlook/Hotmail one. I want you to use the same way to configure Exim to only relay/route emails for hotmail.com/outlook.com etc.

    Waiting for your response.

  • This helped me, however my e-mail wasn’t relaying, it said authentication required – even though everything was configured.

    After a bit more Google’ing, it turns out the hostname/port in /etc/postfix/sasl_passwd must match exactly with /etc/postfix/relay_by_sender. I.e. it needs the [ ] in /etc/postfix/sasl_passwd too:

    smtp-relay.sendinblue.com           smtp_username:smtp_password

    should be:

    [smtp-relay.sendinblue.com]:587            smtp_username:smtp_password
  • this a very good post, thanks a lot.
    I’m using modoboa and everything work well until my emails get blocked by hotmail, outlook.. etc. I research and i can use the SendGrid or the Sendinblue as relay server but i don’t have clear how manage the DNS records required by the relay hosts and the previously created on my domains. I already have the SPF and DKIM configured with the modoboa keys. I can add also the relay hosts DNS records without problem ?

    • Xiao Guoan (Admin)
      3 years ago

      Yes, you can add multiple DKIM records, because each DKIM record has its own selector.
      dkim._domainkey uses the dkim selector.
      mail._domainkey uses the mail selector.

      SPF records can be merged into one record to include new SMTP hosts.

  • Great post – glad I found it. The transport map works fine for simple destinations, but I can’t get your example of how to it for Microsoft destinations to work (with the wildcards). When I try to do a query against the map, it doesn’t match. Here’s one that doesn’t work:

    /.*@hotmail.com/        relay:[smtp-relay.gmail.com]:25

    but

    [email protected]             relay:[smtp-relay.gmail.com]:25

    works fine.

    • Xiao Guoan (Admin)
      3 years ago

      My example is

      /.*@hotmail.*/i             relay:[smtp-relay.sendinblue.com]:587
  • Found the problem: if you want to use regular expressions in the transport map, it must be a ‘regexp’ lookup file, not a ‘hash’. So the ‘main.cf’ configuration is ‘transport_maps = regexp:/etc/postfix/transport’, and all entries in the map must be regular expressions (enclosed in ‘/’s).

    Thanks again for a great article!

  • i dont’t get it working with virtual mailboxes. it seems, as if the transport_maps are not triggered. Any solution/workaround to get it done with virtual mailboxes?

  • Hi there

    I’m having issues with this tutorial, transport is not being parsed according to the filter and additionally “mail transport unavailable” is reported. I have previously setup postfix along with dovecot.

    These are the config files and log

    /etc/postfix/main.cf
    https://pastebin.com/YMemmwTv

    /etc/postfix/master.cf
    https://pastebin.com/u4ruVcpW

    /etc/dovecot/conf.d/10-master.conf
    https://pastebin.com/kRimqyE4

    /var/log/mail.log
    https://pastebin.com/fsTi53tc

    Thanks in advance.

    Martin

  • Great tutorial. Thank you.

    In the section “Set Up SMTP Authentication” you increased the header size…

    header_size_limit = 4096000

    The default value of 102400 seems adequate. Can you provide some reasoning for why you recommend the increase?

  • I didn’t use iRedMail or Modoboa. I’m following the PostfixAdmin virtual mailboxes instructions.

    /etc/postfix/main.cf

    sender_dependent_relayhost_maps = hash:/etc/postfix/relay_by_sender
    transport_maps = hash:/etc/postfix/transport
    
    virtual_mailbox_maps =
       hash:/etc/postfix/transport
       proxy:mysql:/etc/postfix/sql/mysql_virtual_mailbox_maps.cf,
       proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf
    

    /etc/postfix/sasl_passwd

    [relaydestination]:25   smtpUser:smtpPassword
    

    /etc/postfix/transport

    [email protected]  smtp:[relayDesntination]:25
    [email protected] smtp:[relayDesntination]:25
    

    Like your example, I have local emails in postfix and old emails in a third party email server.
    Above are my configurations. Which I based according to your documentation. It works great. However, I want don’t want to list all my old emails under transport but instead relay all emails based on my domain.

    These are the options I’ve tried. One at the time not all together (under etc/postfix/transport):

    mydomain.com        relay:[relayDestination]:25
    *                                  relay:[relayDestination]:25
    *                                   smtp:[relayDestination]:25
    mydomain.com           smtp:[relayDestination]:25
    

    None of the above worked. Am I missing something?

    • Xiao Guoan (Admin)
      2 years ago

      If you followed my PostfixAdmin tutorial, then you just need to add

      transport_maps = hash:/etc/postfix/transport
      

      You shouldn’t change the virtual_mailbox_maps = parameter.

  • For the virtual mailbox example.
    Is there a way to relay all email from my own domain instead of listing every single old email address under /etc/postfix/transport?

    I tried the following (one at the time)/etc/postfix/transport

    mydomain.com   relay:[relayDestination]:25
    *                            relay:[relayDestination]:25
    mydomain.com   smtp:[relayDestination]:25
    *                            smtp:[relayDestination]:25
    

    None of the above worked.

    It only works if I list each email individually:

    [email protected]  smtp:[relayDestination]:25
    [email protected]  smtp:[relayDestination]:25
    
    • Xiao Guoan (Admin)
      2 years ago

      Why do you want to relay your own domain email? I just use the following line in /etc/postfix/transport to use the local delivery agent.

      linuxbabe.com       local
      

      My mail server hosts emails for linuxbabe.com. I don’t want to use a relay to deliver emails to my own domain.

      • I have an Office365 Exchange server which is holds the majority of my emails.
        This postfix server will host just a few additional emails.

        All incoming and outgoing email for my domain goes through Office365 server:
        1.When an email for a postfix user is detected, my Office365 relays the email to Postfix.
        2.All outgoing email from Postfix should first relay to Office365 before going to the internet

        If my domain is stablished as local under /etc/postfix/transport, my Postfix emails cannot communicate to my Office365 emails. This is because it only looks for emails in the postfix MySQL server and when it does not find a matching account, it believes it doesn’t exist and the email is not delivered between the two servers.

        Again, since the majority of my emails reside in Office365 it’s tedious having to list them all under /etc/postfix/transport. I would like to set the entire domain to relay to the relayDestination (Office365) by default…

    • Xiao Guoan (Admin)
      2 years ago

      You are using one domain name and storing most of the email addresses on the Exchange server and some on the Postfix server. This is a bad setup.

      It’s better just to host all email addresses of a domain name on a single server. You can migrate the email addresses to the Postfix server.
      How to Easily Transfer Old Emails to Your New Email Server

  • hi linuxbabe,

    Thanks for this tutorial, which is really helpful. One small error though:

    You are using regular expressions in your /etc/postfix/transport file, so you should use regexp table type in /etc/postfix/main.cf like this:

    transport_maps = regexp:/etc/postfix/transport

    See postfix table types here: https://www.postfix.org/DATABASE_README.html

    Please correct the tutorial, thank you!

  • Zeeshan Mustafa
    1 year ago

    i want to make custom transport in postfix and send emails to specific domain like example.com with delay 2s per emails this rate limit will not effect local domain “slow” is transporter name

    slow_destination_recipient_limit = 2
    slow_destination_rate_delay = 2s

    per domain more than 1 email will go with 2 second delay

    plz help

  • I defined transport as follows ‘transport_maps = hash:/etc/postfix/transport’ in main.cf.
    My transport mappings are

    # cat /etc/postfix/transport | tail -2
    *.example.com local
    * relay:[smtp.gmail.com]:587

    Even though, when i try to send email to [email protected]. It’s keep on relaying gmail.com with port 587.

    Please provide your suggestion.

    Thanks in advance,

  • What is the priority between transport_maps and sender_dependent_relayhost_maps ?

    Let’s imagine if I got a rule for gmail.com in transport_maps (“Use SMTP X) and a another specific rule in sender_dependent_relayhost_maps (“This sender should be relayed through SMTP Y”)

    What will happen if this user tries to send an e-mail to gmail.com ? Which rule will be applied ?

  • Sidharth
    9 months ago

    Dear, We have followed the same above steps for our iRedMail Pro server of sending via different transport.

    but in systemctl status postfix.service

    following error showing

    Sep 12 01:37:28 mail.vinsys.live postfix/qmgr[3832]: warning: connect to transport private/smtp-mail.outlook.com: No such file or directory

    Note: we are using outlook.com SMTP

    Regards,
    Sidharth

Leave a Comment

  • Comments with links are moderated by admin before published.
  • Your email address will not be published.
  • Use <pre> ... </pre> HTML tag to quote the output from your terminal/console.
  • Please use the community (https://community.linuxbabe.com) for questions unrelated to this article.
  • I don't have time to answer every question. Making a donation would incentivize me to spend more time answering questions.

The maximum upload file size: 2 MB. You can upload: image. Links to YouTube, Facebook, Twitter and other services inserted in the comment text will be automatically embedded. Drop file here