Postfix SMTP Multiple Instances with IP Rotation on a Single VPS

This tutorial is going to show you how to set up multiple instances of Postfix SMTP server with IP rotation on a single VPS (virtual private server).

Why Use Multiple IP Addresses

If you send bulk emails using a self-hosted email marketing software like Mautic, it’s a good practice to use multiple IP addresses to spread your email traffic. If mailbox providers observe a large volume of emails coming from a single IP address, then it’s likely to be identified as spam. To maintain good IP reputation, you need to slow down the email traffic sent from a single IP address.

The best Linux VPS hosting provider that allows you to have multiple IP addresses on a single host is Kamatera. The VPS starts at $4/month and each new IP address costs $1/month. Follow the tutorial linked below to create your Linux VPS server with multiple public IP addresses.

Next, you can use iRedMail to quickly set up an email server on the Kamatera VPS.

Once that’s done, come back here to set up Postfix multiple instances with IP rotation.

postfix smtp multiple instance with IP rotation

How many IP addresses should you use for email delivery?

  • If you have 10K email subscribers, 1 IP address is enough.
  • If you need to send 100K emails at once, I recommend at least 5 IP addresses.

Structure of Postfix Multiple Instances

Postfix SMTP server can be configured to run multiple instances on a single host. All instances share the same program files, but use a unique directory for the following files.

  • Configuration files
  • Spool/queue
  • Data

This tutorial shows you the process of setting up 2 Postfix instances. You can use the same process to add a third, fourth, fifth… instance.

Step 1: Set Up Multiple Postfix Instances

Run the following command to initialize multi-Instance in Postfix.

sudo postmulti -e init

This will add the following two lines at the bottom of the /etc/postfix/main.cf file.

multi_instance_wrapper = ${command_directory}/postmulti -p --
multi_instance_enable = yes

Next, create a new Postfix instance.

sudo postmulti -e create -I postfix-smtp1

where:

  • -e: edit mode
  • -I: instance name, which must start with postfix-.

It automatically creates the /etc/postfix-smtp1/ directory, which includes the master.cf and main.cf file. The /etc/postfix-smtp1/main.cf file has the following lines at the bottom.

readme_directory = no
inet_protocols = ipv4
master_service_disable = inet
authorized_submit_users =
queue_directory = /var/spool/postfix-smtp1
multi_instance_name = postfix-smtp1

Change

master_service_disable = inet

to

master_service_disable =

So the postfix-smtp1 instance can bind to a TCP socket. Each Postfix instance must have a unique value for the following two parameters.

myhostname = 
inet_interfaces =

For example, the primary instance (/etc/postfix/main.cf) can use

myhostname = smtp.example.com
inet_interfaces = 11.22.33.44

The postfix-smtp1 instance can use

myhostname = smtp01.example.com
inet_interfaces = 11.22.33.55

So each instance has a unique hostname and binds to different IP address.

  • It’s recommended to add your all IP addresses to the mynetworks parameter.
  • If you have added other custom settings (like TLS configurations) in the /etc/postfix/main.cf file, you should copy them to the /etc/postfix-smtp1/main.cf file.
  • The /etc/postfix-smtp1/master.cf contains the stock content provided by Postfix. Again, you can copy the custom settings from the /etc/postfix/master.cf file.

Next, create an etc directory under the queue directory.

sudo mkdir -p /var/spool/postfix-smtp1/etc/

Copy the files from the primary instance.

sudo cp -r /var/spool/postfix/etc/* /var/spool/postfix-smtp1/etc/

Then run the following command to enable the postfix-smtp1 instance.

sudo postmulti -i postfix-smtp1 -e enable

Now restart the primary instance.

sudo systemctl restart postfix

Then start the postfix-smtp1 instance.

sudo postmulti -i postfix-smtp1 -p start

If your config file has duplicate parameters, this command will show you, and you need to remove the duplicate ones. Possible duplicate parameters are:

mynetworks = 
inet_protocols =

If the postfix-smtp1 instance won’t start, check the mail log file (/var/log/mail.log or /var/log/maillog) to see what went wrong.

If you want to stop it, run

sudo postmulti -i postfix-smtp1 -p stop

To reload configuration files, run

sudo postmulti -i postfix-smtp1 -p reload

To list mail queue for the postfix-smtp1 instance, run

sudo postqueue -c /etc/postfix-smtp1/ -p

Note that the sudo systemctl restart postfix command will only restart the primary instance. To list all Postfix instances, run

sudo postmulti -l -a

Run the following command to check if Postfix is listening on the correct IP addresses.

sudo ss -lnpt | grep :25

If everything is configured correctly, the two instances should be listening to its own IP address.

postmulti linux server

Step 2: Set Up DNS Load Balancing

You can use a separate hostname for your VPS and create two DNS A records for the hostname. This is called DNS load balancing.

smtp.example.com   A   11.22.33.44
                   A   11.22.33.55

Then configure your SMTP clients (such as your self-hosted email marketing application) to use the hostname (smtp.example.com). It will try the two IP addresses in a round-robin fashion.

Step 3: Set Up Firewall

By default, your server has a default gateway, and it always uses the gateway IP address to connect to other servers. To achieve better email deliverability, we need to make the following happen.

  • If an SMTP client connects to the first IP address on your VPS, then the VPS should use the first IP address to send the email.
  • If an SMTP client connects to the second IP address on your VPS, then the VPS should use the second IP address to send the email.

To make this happen, we need to configure SNAT (Source NAT) in the server firewall. SNAT changes the source IP address.

There are several firewall software on Linux. The following are the common ones.

  • iptables: The most well-known firewall in the Linux world.
  • nftables: The new kid on the block that’s supposed to replace iptables.
  • UFW: a wrapper for iptables, commonly seen on Debian/Ubuntu servers.
  • Firewalld: a wrapper for iptables/nftables, commonly seen on RHEL, CentOS, Rocky Linux, Alma Linux servers.

iptables

If you use iptables, then you need to run the following two commands to set up SNAT.

sudo iptables -t nat -A POSTROUTING -s 11.22.33.44 -p tcp --dport 25 -j SNAT --to-source 11.22.33.44
sudo iptables -t nat -A POSTROUTING -s 11.22.33.55 -p tcp --dport 25 -j SNAT --to-source 11.22.33.55

This means

  • If the connection is originated from the first IP address, then use it as the source IP address. The recipient’s SMTP server will think the email comes from the first IP address.
  • If the connection is originated from the second IP address, then use it as the source IP address. The recipient’s SMTP server will think the email comes from the second IP address.

UFW

If you use UFW firewall, edit the /etc/ufw/before.rules file.

sudo nano /etc/ufw/before.rules

Add the following lines at the bottom of this file. Replace the IP address as necessary.

# NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 11.22.33.44 -p tcp --dport 25 -j SNAT --to-source 11.22.33.44
-A POSTROUTING -s 11.22.33.55 -p tcp --dport 25 -j SNAT --to-source 11.22.33.55

# End each table with the 'COMMIT' line or these rules won't be processed
COMMIT

postfix ufw SNAT

Save and close the file. Then restart UFW.

sudo systemctl restart ufw

Firewalld

If you use Firewalld, then run the following two commands to set up SNAT. Replace the IP address as necessary.

sudo firewall-cmd --permanent --direct --add-rule ipv4 nat POSTROUTING 0 -s 11.22.33.44 -p tcp --dport 25 -j SNAT --to-source 11.22.33.44

sudo firewall-cmd --permanent --direct --add-rule ipv4 nat POSTROUTING 1 -s 11.22.33.55 -p tcp --dport 25 -j SNAT --to-source 11.22.33.55

Reload Firewalld for the changes to take effect.

sudo systemctl reload firewalld

Step 4: Set Up SMTP Fallback Relay

What if your outgoing email is bounced, causing email delivery failure? You can tell Postfix to fall back to another SMTP instance, and the email will be re-delivered using another IP address on your VPS. Your SMTP server will rotate sending IP addresses to minimize failed email delivery. This technique is also used by Gmail.

For example, you can open the main.cf file of the primary Postfix instance.

sudo nano /etc/postfix/main.cf

Add the following line at the end of this file.

smtp_fallback_relay = [11.22.33.55]:25

In this way, the primary Postfix instance will try delivering the email by itself, and if the delivery failed, it will be passed to the second Postfix instance for delivery.

smtp_fallback_relay is not perfect, because it only works when the email is soft bounced (code 4xx). If your outgoing email is hard bounced (code 5xx), then Postfix won’t use the fallback relay.

Fortunately, you can change the SMTP reply code from 5xx to 4xx, so Postfix will think all bounced emails are soft bounces and it will use the fallback SMTP relay. To make this work, add the following line at the bottom of the /etc/postfix/main.cf file.

smtp_reply_filter = pcre:/etc/postfix/smtp_reply_filter

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

sudo nano /etc/postfix/smtp_reply_filter

Add the following line in this file. Please don’t add any whitespace at the beginning of this line.

/^5(.*)$/ 4$1

Save and close the file. Next, build this lookup table.

sudo postmap /etc/postfix/smtp_reply_filter

Install the PCRE map support for Postfix.

  • Debian/Ubuntu: sudo apt install postfix-pcre
  • CentOS/RHEL/Rocky Linux: sudo dnf install postfix-pcre

Make sure you have added all IP addresses to the Postfix mynetworks parameter in the main.cf file, or the SMTP relay won’t work. You need to do this for all Postfix instances.

Restart Postfix for the changes to take effect.

sudo systemctl restart postfix

Step 5: Add PTR Record for Each IP Address

Kamatera doesn’t allow you to edit PTR record in the control panel. Instead, you need to open a support ticket and tell them to add PTR record for you. It’s not convenient, you might think, but this is to keep spammers away from the platform, so legitimate email senders like us will have a great IP reputation.

Step 6: Configure SMTP Authentication

Now we need to configure SMTP authentication for the postfix-smtp1 Postfix instances.

sudo nano /etc/postfix-smtp1/master.cf

Add the following lines to this file, so SMTP clients can authentication on TCP port 587 or 465 and submit outgoing emails.

submission     inet     n    -    y    -    -    smtpd
 -o syslog_name=postfix/submission
 -o smtpd_tls_security_level=encrypt
 -o smtpd_tls_wrappermode=no
 -o smtpd_sasl_auth_enable=yes
 -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
 -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
 -o smtpd_sasl_type=dovecot
 -o smtpd_sasl_path=private/auth


smtps     inet  n       -       y       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
  -o smtpd_sasl_type=dovecot
  -o smtpd_sasl_path=private/auth

Save and close the file. Then stop postfix-smtp01 instance.

sudo postmulti -i postfix-smtp1 -p stop

And start it again.

sudo postmulti -i postfix-smtp1 -p start

Then you need to configure a separate Dovecot SMTP auth socket for the postfix-smtp1 instance.

sudo nano /etc/dovecot/conf.d/10-master.conf

Your service auth section might look like this:

service auth {
    unix_listener /var/spool/postfix/private/auth {
      mode = 0660
      user = postfix
      group = postfix
    }
}

You need to add a new unix_listener for the postfix-smtp1 instance, which uses /var/spool/postfix-smp1/ directory as the spool directory.

service auth {
    unix_listener /var/spool/postfix/private/auth {
      mode = 0660
      user = postfix
      group = postfix
    }

   unix_listener /var/spool/postfix-smtp1/private/auth {
      mode = 0660
      user = postfix
      group = postfix  
   }
}

Save and close the file. Then restart Dovecot.

sudo systemctl restart dovecot

Step 7: Testing

Now you can send test emails to see if it’s working.

Troubleshooting

If you see the following error in the mail log (/var/log/mail.log or /var/log/maillog)

postfix/submission/smtpd[4125]: warning: SASL: Connect to private/auth failed: No such file or directory
postfix/submission/smtpd[4125]: fatal: no SASL authentication mechanisms

This means you didn’t add a new unix_listener for the postfix-smtp1 instance in the Dovecot 10-master.conf file.

No DKIM Signature?

If you use OpenDKIM, but there’s no DKIM signature in your email, then you should use the following settings.

Open the OpenDKIM configuration file.

sudo nano /etc/opendkim.conf

If your file has a line like below, then OpenDKIM is using Unix socket to accept connections from Postfix.

Socket   local:/var/spool/postfix/opendkim/opendkim.sock

Comment it out. We need to configure OpenDKIM to use TCP/IP sockets.

Socket    inet:8891@localhost

Save and close the file. Next, edit the main.cf file of each Postfix instance, add the following lines in the file, so Postfix will connect to OpenDKIM via TCP/IP socket.

# Milter configuration
milter_default_action = accept
milter_protocol = 6
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = $smtpd_milters

Save and close the file. Then restart each Postfix instance.

Enable Autostart

The main Postfix instance can automatically start at boot time.

sudo systemctl enable postfix

But this doesn’t make other Postfix instances autostart. We need to create a systemd service to make it autostart.

sudo nano /etc/systemd/system/postfix-smtp1.service

Add the following lines to this file.

[Unit]
Description=Start the postfix-smtp1 instance
After=network.target

[Service]
Type=forking
ExecStartPre=/bin/sleep 5
ExecStart=/bin/bash -c '/usr/sbin/postmulti -i postfix-smtp1 -p start'
ExecStop=/bin/bash -c '/usr/sbin/postmulti -i postfix-smtp1 -p stop'
ExecReload=/bin/bash -c '/usr/sbin/postmulti -i postfix-smtp1 -p reload'
Restart=on-failure

[Install]
WantedBy=multi-user.target

Save and close the file. Then reload systemd.

sudo systemctl daemon-reload

Enable autostart at boot time.

sudo systemctl enable postfix-smtp1

Stop the current postfix-smtp1 instance.

sudo postmulti -i postfix-smtp1 -p stop

Now you can start it with systemd

sudo systemctl start postfix-smtp1

How to Bypass Email Blacklists

Your outgoing emails might be rejected due to IP address blacklisting. Even if you don’t send spam, sometimes your email server IP address can be blacklisted for reasons out of your control.  Follow the tutorial below to learn how to get around blacklists.

Wrapping Up

I hope this tutorial helped you set up multiple instances of Postfix SMTP server with IP rotation on a single VPS. As always, if you found this post useful, then subscribe to our free newsletter to get more tips and tricks. Take care 🙂

Rate this tutorial
[Total: 7 Average: 5]

9 Responses to “Postfix SMTP Multiple Instances with IP Rotation on a Single VPS

  • Hi,
    Thanks for your tutorial. I used a multi-domain certificate, following the previous tutorial. Each IP address is bound to a sending domain name. But I have a problem that other than the main domain name cannot send mail with ssl encryption. To be precise, the server verification certificate only has the certificate of the main domain name, and the certificate under the main domain name cannot be verified. So the sending fails.

    The issue:

    Connection could not be established with host mail.xxx.xxx:stream_socket_client(): SSL: Connection reset by peer Log data: ++ Starting Swift_SmtpTransport !! Connection could not be established with host mail.xxx.xxx :stream_socket_client(): SSL: Connection reset by peer (code: 0)
    ++ Starting Swift_SmtpTransport !! Connection could not be established with host mail.xxx.xxx :stream_socket_client(): SSL: Connection reset by peer (code: 0)
    

    I have detected the server openssl corresponding as follows

    root@mail:~# openssl s_client -starttls smtp -connect mail.xxx.xxx:465
    CONNECTED(00000003)
    Didn't find STARTTLS in server response, trying anyway...
    write:errno=32
    ---
    no peer certificate available
    ---
    No client certificate CA names sent
    ---
    SSL handshake has read 0 bytes and written 23 bytes
    Verification: OK
    ---
    New, (NONE), Cipher is (NONE)
    Secure Renegotiation IS NOT supported
    Compression: NONE
    Expansion: NONE
    No ALPN negotiated
    Early data was not sent
    Verify return code: 0 (ok)
    

    How to fix this problem?Thank you.

    • Xiao Guoan (Admin)
      2 years ago

      For email submission, STARTTLS should be used with port 587. Port 465 should be used when you want to use the SMTPS protocol.

      Also be sure to copy the SSL/TLS setting from /etc/postfix/main.cf file to /etc/postfix-smtp1/main.cf.

  • Hi mail is only sending primary smtp not postfix-smtp1. Need to change anything or rate limit.

    • Xiao Guoan (Admin)
      2 years ago

      Are you using Debian, Ubuntu or CentOS/Rocky Linux?

  • It’s CentOS 7.

  • Thank you. It’s very advance and hard for me. Do you have any package that can be installed and can do all of this without the manual configuration? I already installed iRedMail and I want to make SMTP rotation on the same email server installed, is it possible? Thanks.

  • raptor
    1 year ago

    hi,
    sorry for the silly question and I’m very new to this but can to tell me where change myhostname = smtp.example.com
    inet_interfaces = for smtp and smtp1.
    thank you.

  • This is a great article. Thanks for writing it.

    If you are like me and not spamming the universe with marketing emails and you just want a way to relay email from a datacenter to the outside world another configuration option is running each instance on a different port by defining a different service in the master.cf – i.e. “smtp” and “2525”)

    Configured this way you don’t need unique myhostname and inet_interfaces defined.

    I use this to only allow relay locally (from mydomain.com to mydomain.com) anything received on port 2525. On port 25 I relay to any destination but only from a defined network list – “mynetworks=192.168.1.1/24, 10.10.2.5”.

  • Thank you for this brilliant article, I’ve been using it for a while. Some questions from me:

    What’s the best way to set up DNS MX records? Does it matter?

    Under Fallback Relay, you write “Your SMTP server will rotate sending IP addresses to minimize failed email delivery”. How does this work? In the example there is one specific fallback IP address.

    Is there a risk of an infinite loop when setting up fallback relay on all instances?

    Thank you again!

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