Set Up SMTP & IMAP Proxy with HAProxy (Debian, Ubuntu, CentOS)

In previous tutorials, we discussed how to set up a mail server from scratch on Linux (Ubuntu version, CentOS/RHEL version), and how to use iRedMail or Modoboa to quickly set up your own mail server without having to manually configure each component of the mail server stack. This tutorial is going to show you how to set up SMTP and IMAP proxy for your mail server with HAProxy.

When Do You Need SMTP and IMAP Proxy?

Some folks run email servers at home, but may have the following problems:

  • Port 25 is blocked.
  • They don’t have a static IP address.
  • They can’t create PTR record.

If port 25 is blocked, you can’t send emails directly to recipients. And if you don’t have a static IP address or PTR record, your emails are likely to be rejected or land into the spam folder. If you are in this situation, you can run a VPS (Virtual Private Server) at a data center and use it as a proxy for your mail server. The VPS has a static IP address and you can create PTR record for the IP address. Other email servers would think that the VPS runs your mail service and when you send an email, they would think the email comes from your VPS.

Set Up SMTP and IMAP Proxy with HAProxy

Yes, you can also use SMTP relay services such as Sendinblue to solve these problems, but there’s a limit on how many emails you can send each day and each month. If you upgrade to a paid account with Mailjet, it costs at least $25 each month. The more emails you send, the higher your monthly cost will be. If you run a VPS and set up mail proxy, it costs about $10 per month no matter how many emails you are going to send.

If you run a mail server for lots of people, you might need to set up mail proxy for load balancing and high availability. In this article, I will set up SMTP and IMAP proxy with HAProxy, which is a free, open-source high availability load balancer and proxy server for TCP and HTTP-based applications.

Step 1: Choose the Right VPS for Mail Proxy

You need a VPS that

  • allows you to create PTR record
  • doesn’t block port 25
  • allows you to send unlimited emails without restrictions.

Not all VPS providers meet the above 3 requirements. For example, DigitalOcean blocks port 25 and it would not unblock port 25. Another problem is that big well-known hosting providers like DigitalOcean are abused by spammers. Often the server IP address is on several blacklists.

I run my mail servers on ScalaHosting and Kamatera VPS. I always recommend them when setting up a mail server. For a mail proxy that doesn’t require much CPU and RAM, you can choose Kamatera VPS. The 1 CPU, 1GB RAM plan costs only $4/month, and you will get one month for free. You can follow the tutorial below to create a Kamatera VPS.

You can choose any Linux distro for your VPS, but I recommend you to use Debian, Ubuntu, or CentOS.

To log into your server, you use an SSH client. If you are using Linux or macOS on your computer, then simply open up a terminal window and run the following command to log into your server. Replace 12.34.56.78 with the IP address of your VPS.

ssh [email protected]12.34.56.78

You will be asked to enter the password. If you are using Windows, please read the following article on how to use SSH client.

Step 2: Set Up VPN Server on Your VPS

If you have a dynamic IP address at your home, then you need to set up a VPN server on your VPS, so your VPS will be able to communicate with your mail server without being interrupted due to the change of IP address. The VPN server can also help you bypass port 25 blocking.

You can set up WireGuard VPN on your VPS by following one of the tutorials below. Why do I choose WireGuard instead of other VPN protocols like OpenVPN? Because WireGuard allows you to assign static private IP addresses to VPN clients.

When following the instructions in the above articles, your VPS is the VPN server and your mail server is the VPN client. The VPS will become the default gateway for your mail server and all outbound traffic on your mail server will be tunneled through VPN, so receiving SMTP servers (Gmail, Hotmail, Yahoo Mail, etc) will think your emails come from the VPS. If you want to send outgoing emails via the VPS, but make other types of traffic use the original gateway, WireGuard also allows you to do so by enabling policy routing.

You should set a PTR record, aka reverse DNS record, for your VPS. 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. Tell the support team to update the PTR record of your server IP address to mail.your-domain.com.

Step 3: Open Ports in Firewall and Set Up Permissions

The VPS needs to open port 25, 587, 465, 143 and 993 in the firewall. Run the following commands to open these ports.

Debian/Ubuntu:

sudo ufw allow 25,587,465,143,993/tcp

CentOS:

sudo firewall-cmd --permanent --add-service={smtp,smtp-submission,smtps,imap,imaps}

sudo systemctl reload firewalld

The mail server needs to open various ports to the VPS. Run the following command.

Debian/Ubuntu:

sudo ufw insert 1 allow in from 10.10.10.0/24

CentOS:

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.10.10.0/24" accept'

sudo systemctl reload firewalld

10.10.10.0/24 is the private IP range created by the VPN server, so the VPS can access all ports on the mail server.

Configure SELinux on CentOS

Later in this tutorial, HAProxy on the VPS needs to bind to various email ports like 25, 587, 465, 143 and 993, but is prevented from doing so by SELinux. If you use CentOS on the VPS, you need to run the following command to allow HAProxy to bind to these ports.

sudo setsebool -P haproxy_connect_any 1

Step 4: Set Up SMTP Proxy to Receive Email

Now you need to set up SMTP proxy so that other mail servers can send emails to your own mail server via the VPS. SSH into your VPS and install HAProxy.

Debian/Ubuntu

sudo apt install haproxy

CentOS

sudo dnf install haproxy

Then edit the HAProxy main configuration file.

sudo nano /etc/haproxy/haproxy.cfg

Add the following lines at the end of the file. Replace 12.34.56.78 with the public IP address of your VPS. Replace 10.10.10.101 with the private IP address of your mail server, which is assigned by your VPN server.

frontend ft_smtp
      bind 12.34.56.78:25
      mode tcp
      timeout client 1m
      log global
      option tcplog
      default_backend bk_smtp

backend bk_smtp
      mode tcp
      log global
      option tcplog
      timeout server 1m
      timeout connect 7s
      server postfix 10.10.10.101:2525 send-proxy

The above configuration will make HAProxy listen on port 25 and pass SMTP connections to port 2525 of your mail server. Save and close the file. Restart HAProxy.

sudo systemctl restart haproxy

And enable auto-start at boot time.

sudo systemctl enable haproxy

To use HAProxy as a reverse proxy for the Postfix SMTP server, you need to enable Postscreen in Postfix. SSH into your mail server and edit the Postfix master configuration file.

sudo nano /etc/postfix/master.cf

Add the following lines at the beginning of this file. Replace 10.10.10.101 with the private IP address of your mail server assigned by VPN server. This will enable Postscreen on port 2525 and it can accept HAProxy connections from your VPS. Postfix is able to obtain the original IP address of SMTP client from HAProxy.

10.10.10.101:2525      inet  n       -       -       -       1       postscreen
  -o postscreen_upstream_proxy_protocol=haproxy
  -o postscreen_cache_map=btree:$data_directory/postscreen_2525_cache
  -o syslog_name=postfix/2525

Then uncomment the following 3 lines. (Note: If you use iRedMail or Moboboa to run your mail server, then the following 3 lines are uncommented by default.)

smtpd     pass  -       -       y       -       -       smtpd
dnsblog   unix  -       -       y       -       0       dnsblog
tlsproxy  unix  -       -       y       -       0       tlsproxy

Where:

  •  The first line will make Postscreen pass SMTP connection to smtpd daemon.
  • The dnsblog (DNS Blacklist Logger) service enables logging of DNS blacklist checks.
  • The tlsproxy service enables STARTTLS support for postscreen, so remote SMTP clients can establish encrypted connection when Postscreen is enabled.

postfix haproxy smtp proxy

Save and close the file. Restart Postfix for the change to take effect.

sudo systemctl restart postfix

Now add a new MX record for your domain name like below, and your mail server is able to receive emails via the VPS.

Record Type    Name      Mail Server            Priority

MX             @         hostname-of-your-VPS     0

You can use any hostname for your VPS, as long as it can be resolved to the IP address of your VPS. For simplicity, you can use the hostname of the mail server (mail.yourdomain.com). Don’t forget to add DNS A record for the hostname of your VPS.

Step 5: Set Up Submission Proxy

Your users can submit outgoing emails to your mail server without proxy, but what if you want your users to be able to submit outgoing emails through the VPS? You need to set up proxy for the Postfix submission service.

Edit the HAProxy main configuration file on your VPS.

sudo nano /etc/haproxy/haproxy.cfg

Add the following lines at the end of the file. Replace 12.34.56.78 with the public IP address of your VPS. Replace 10.10.10.101 with the private IP address of your mail server, which is assigned by your VPN server.

frontend ft_submission
      bind 12.34.56.78:587
      mode tcp
      timeout client 1m
      log global
      option tcplog
      default_backend bk_submission

backend bk_submission
      mode tcp
      log global
      option tcplog
      timeout server 1m
      timeout connect 7s
      server postfix 10.10.10.101:10587 send-proxy

frontend ft_smtps
      bind 12.34.56.78:465
      mode tcp
      timeout client 1m
      log global
      option tcplog
      default_backend bk_smtps

backend bk_smtps
      mode tcp
      log global
      option tcplog
      timeout server 1m
      timeout connect 7s
      server postfix 10.10.10.101:10465 send-proxy

There are commonly two ports that can accept email submissions from authenticated users: 587 and 465. So in the above configuration, we defined two front ends in HAProxy listening on port 587 and 465. They will pass connections to port 10587 and 10465 of your mail server, respectively.

Save and close the file. Restart HAProxy.

sudo systemctl restart haproxy

Then edit Postfix master configuration file on your mail server.

sudo nano /etc/postfix/master.cf

Add the following lines at the end of this file. Replace 10.10.10.101 with the private IP address of your mail server, which is assigned by your VPN server. Please allow at least one whitespace (tab or spacebar) before -o.  In postfix configurations, a preceding whitespace character means that this line is continuation of the previous line. However, you should not add space before or after the equal sign (=).

10.10.10.101:10587     inet     n    -    y    -    -    smtpd
  -o syslog_name=postfix/10587
  -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
  -o smtpd_upstream_proxy_protocol=haproxy

10.10.10.101:10465     inet  n       -       y       -       -       smtpd
  -o syslog_name=postfix/10465
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
  -o smtpd_sasl_type=dovecot
  -o smtpd_sasl_path=private/auth
  -o smtpd_upstream_proxy_protocol=haproxy

In the above configuration, we enabled two submission services listening on port 10587 and 10465, and they support the haproxy protocol, so they will be able to accept connections from HAProxy. Save and close the file. Restart Postfix for the change to take effect.

sudo systemctl restart postfix

Step 6: Set Up IMAP Proxy

We also want users to be able to log into the IMAP server via the VPS, so we need to set up IMAP proxy.

Edit the HAProxy main configuration file on your VPS.

sudo nano /etc/haproxy/haproxy.cfg

Add the following lines at the end of the file. Replace 12.34.56.78 with the public IP address of your VPS. Replace 10.10.10.101 with the private IP address of your mail server, which is assigned by your VPN server.

frontend ft_imap
    bind 12.34.56.78:143
    mode tcp
    default_backend bk_imap

backend bk_imap
    mode tcp
    balance leastconn
    stick store-request src
    stick-table type ip size 200k expire 30m
    server imap1 10.10.10.101:10143 send-proxy-v2

frontend ft_imaps
    bind 12.34.56.78:993
    mode tcp
    default_backend bk_imaps

backend bk_imaps
    mode tcp
    balance leastconn
    stick store-request src
    stick-table type ip size 200k expire 30m
    server imaps1 10.10.10.101:10993 send-proxy-v2

There are two ports for the IMAP service: 143 and 993. Port 143 can use STARTTLS and port 993 uses implicit TLS, so in the above configuration, we added two front ends in HAproxy listening on port 143 and 993. They will pass connections to port 10143 and 10993 of your mail server, respectively. Save and close the file. Restart HAProxy.

sudo systemctl restart haproxy

Edit Dovecot configuration file on your mail server.

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

Add HAProxy support for IMAP and IMAPS like below.

service imap-login {
  inet_listener imap {
    port = 143
  }
  inet_listener imaps {
    port = 993
    ssl = yes
  }

  inet_listener imap_haproxy {
    port = 10143
    haproxy = yes
  }
  inet_listener imaps_haproxy {
    port = 10993
    ssl = yes
    haproxy = yes
  }
}

In the above configuration, we enabled two IMAP services: imap_haproxy and imaps_haproxy, listening on port 10143 and 10193, respectively. They support the haproxy protocol, so they will be able to accept connections from HAProxy. Save and close the file.

Then we need to add trusted proxy hosts in Dovecot. Edit the Dovecot main configuration file.

sudo nano /etc/dovecot/dovecot.conf

Add the following two lines at the end of this file. Replace 10.10.10.1 with the private IP address of your VPN server.

haproxy_trusted_networks = 10.10.10.1
haproxy_timeout = 3s

Save and close the file. Restart Dovecot for the change to take effect.

sudo systemctl restart dovecot

Now you should be able to log into the IMAP server and submit outgoing emails via the VPS.

Step 7: Set Up HTTPS Proxy

If you would like to access webmail such as Roundcube via the VPS, then edit the HAProxy main configuration file on your VPS.

sudo nano /etc/haproxy/haproxy.cfg

Add the following lines at the end of the file. Replace 12.34.56.78 with the public IP address of your VPS. Replace 10.10.10.101 with the private IP address of your mail server, which is assigned by your VPN server. Replace mail.yourdomain.com with the domain name used by your webmail.

frontend https
   bind 12.34.56.78:443
   mode tcp
   tcp-request inspect-delay 5s
   tcp-request content accept if { req_ssl_hello_type 1 }

   use_backend webmail if { req_ssl_sni -i mail.yourdomain.com }
   default_backend webmail

backend webmail
   mode tcp
   option ssl-hello-chk
   server webmail 10.10.10.101:443 check

Save and close the file. Then restart HAProxy.

sudo systemctl restart haproxy

Note that the HTTPS proxy can interfere with Let’s Encrypt TLS certificate renewal if you use the default http-01 challenge in certbot. It’s recommended to use dns-01 challenge when you renew your Let’s Encrypt TLS certificate with certbot.

Final Thoughts

Notice that we enabled proxy support for Postfix and Dovecot by adding more listening ports (2525, 10587, 10465, 10143, 10993). We didn’t enable proxy support for existing ports (25, 587, 465, 143 and 993), because if we do, then Postfix and Dovecot will accept connections from HAProxy only and deny connection from other IP addresses, including localhost. This can prevent your webmail or web application running on the mail server to use 127.0.0.1:25 to send emails, and prevent the webmail client from fetching emails from Dovecot. You will probably see the following error when this happens.

host mail.example.com refused to talk to me: 421 4.3.2 No system resources

And your Postfix SMTP server would log the following message in the mail log.

postfix/postscreen[1479]: warning: haproxy read: time limit exceeded

Configure HAProxy Automatic Restart

I found the haproxy.service on CentOS/RHEL can fail to start at boot time. The error is as follows.

Starting frontend ft_smtp: cannot bind socket [23.254.225.226:25]

If I manually start the service, it works, which is confusing to me. To solve this issue, we can edit the haproxy.service to make it automatically restart on failure. To override the default systemd service configuration, we create a separate directory.

sudo mkdir -p /etc/systemd/system/haproxy.service.d/

Then create a file.

sudo nano /etc/systemd/system/haproxy.service.d/restart.conf

Add the following lines in the file.

[Service]
Restart=always
RestartSec=5s

Save and close the file. Then reload systemd.

sudo systemctl daemon-reload

Postfix/Dovecot Automatic Restart

It’s also recommended to configure Postfix and Dovecot on the mail server to automatically restart on failure.

Postfix

Create a separate directory.

sudo mkdir -p /etc/systemd/system/postfix.service.d/

Then create a file.

sudo nano /etc/systemd/system/postfix.service.d/restart.conf

Add the following lines in the file. Note that on Debian/Ubuntu, the postfix.service is an oneshot service, which doesn’t allow Restart=always.

[Service]
Restart=on-failure
RestartSec=5s

Save and close the file. Then reload systemd.

sudo systemctl daemon-reload

Dovecot

Create a separate directory.

sudo mkdir -p /etc/systemd/system/dovecot.service.d/

Then create a file.

sudo nano /etc/systemd/system/dovecot.service.d/restart.conf

Add the following lines in the file.

[Service]
Restart=always
RestartSec=5s

Save and close the file. Then reload systemd.

sudo systemctl daemon-reload

IP Blacklist Removal

What if the IP address of your VPS is blacklisted by a particular email service provider? You can read the following article to learn how to get your IP address removed from blacklists.

Troubleshooting Tips

If you send email to your mail server, but received the following error,

lost connection with mail.yourdomain.com[xx.xx.xx.xx] while receiving the initial server greeting

it’s probably because your VPN connection is broken, verify if you can ping from the VPN server to the VPN client (mail server).

ping 10.10.10.101

It could also be that you didn’t configure the mail server firewall to allow connection from the VPN (10.10.10.0/24). Run the following command on the VPN server to see if the ports are open. Nmap can be installed on the VPN server with sudo apt install nmap.

sudo nmap 10.10.10.101

Verify if you can telnet to the mail server on port 2525.

telnet 10.10.10.101 2525

Sometimes, you may have an error in your HAProxy configurations. For example, I once had two default_backend directives in HAProxy as follows.

frontend ft_smtp
    bind 12.34.56.78:25
    mode tcp
    timeout client 1m
    log global
    option tcplog
    default_backend bk_smtp

default_backend ocserv

This is wrong. If you define a default_backend in a frontend section, then you can’t define another default_backend in the global section. Instead, you should place each default_backend directive in the appropriate frontend section like we did in this article.

Another config error is that you might accidentally insert other directives in the https frontend.

frontend https
   bind 12.34.56.78:443
   mode tcp
   tcp-request inspect-delay 5s
   tcp-request content accept if { req_ssl_hello_type 1 }

   some_other_directives

   use_backend webmail if { req_ssl_sni -i mail.yourdomain.com }
   default_backend webmail

Alternative Method

If you can’t figure out what’s wrong in your server environment, you can also set up port forwarding with UFW firewall on the VPN server. Traffic will be forwarded by UFW, So HAProxy won’t be used at all. (You don’t need to remove HAProxy.)

The following ports should be forwarded from the VPN server to the mail server.

  • TCP 25
  • TCP 587
  • TCP 465
  • TCP 143
  • TCP 993
  • TCP 80
  • TCP 443
  • TCP 110
  • TCP 995

Conclusion

I hope this tutorial helped you set up SMTP and IMAP proxy. 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: 6 Average: 5]

18 Responses to “Set Up SMTP & IMAP Proxy with HAProxy (Debian, Ubuntu, CentOS)

  • Michael
    1 year ago

    Nice tutorial.

  • Great content and Very nice write-up for beginners. 赞

  • Very helpful tutorial. This is what I have been looking to configure for a long time.

    One question, I did not see any passing of port 80 / 443 through the VPS to the server so that web interface could be accessed through the VPS without opening my home firewall. Would you pass those items through HAProxy to your home server so that you can have LetsEncrypt issue certificates to use with the mail server? Based on my reading we would allow the home server to handle the SSL.

    Just trying to avoid opening those ports up to my home network.

    Thank you.

  • A very good job

  • Thanks, Xiao. Very nice tutorial, as always.
    I was looking for a solution to bring a currently VPS-based postfix server to my private LAN, and this article gave me an idea.

    Just wonder how do you configure the private mail server to send outgoing emails to public mail servers via the HAProxy, as shown by the red path in the top image?

    • Hi, so I did a little research and found 2 possible solutions:

      (1) I can install postfix on my public VPS, where I’ve installed HAProxy, and configure it as a relay server. Then on the private mail server, I would configure postfix “relayhost” accordingly.

      (2) I can configure the private mail server to route outgoing traffic with destination port 25 to my public VPS after enabling IP forwarding there to have it act as a NAT gateway.

      Solution 2 is preferred as it won’t expose email content to the VPS server along the path.

      How do you think? Any other solution?

    • Hello Tho,

      In this tutorial, the mail server establishes a VPN connection to the VPS, so all traffic (including outgoing emails) will be routed through the VPS.

  • Great tutorial! However I have an issue where when I send an email to an external source, such as gmail.com, outlook, etc. I get an error – “mail for [recipient domain] loops back to myself

    • Check the mydestination variable. It should only contain your own domains.

      postconf mydestination
      • I have confirmed that but I am still having issues.. I think I might restart everything, mail server, wireguard setup, etc.

        If I followed all three of your guides word for word on how to create a mail server, wireguard server and of course the haproxy setup, will I have any luck? Is this how you went about it?

        I really appreciate this guide and doing this has already taught me a lot about networking so I am happy even with my current failure. I just really want to make this a success.

    • I think you don’t need to restart everything. The problem is either in Postfix, your DNS records, or a DNS resolver issue.
      I have seen this error on my own server in the past. It’s a simple fix, but I don’t remember exactly what I did.

      • Thank you so much!

        It turns out I either had a misconfiguration in postfix, dovecot or wireguard.

        I am pleased to say that everything works just perfectly by following your three guides on:
        Creating an email server.
        Creating a wireguard server (and client)
        And of course, this guide.

        Thank you for the information, you’re a great writer. Best of luck to you Xiao!

    • You can try the following command on your mail server to find out what’s the MX host for gmail.com

      dig MX gmail.com +short

      A correct output should be

      10 alt1.gmail-smtp-in.l.google.com.
      5 gmail-smtp-in.l.google.com.
      30 alt3.gmail-smtp-in.l.google.com.
      40 alt4.gmail-smtp-in.l.google.com.
      20 alt2.gmail-smtp-in.l.google.com.
      

      Then query the A record.

      dig A gmail-smtp-in.l.google.com +short
  • Hi,
    Great writeup,

    I’m having a bit of trouble with roundcube not working.
    After going through the logs I was able to determine that port 143 on localhost was not responding.

     telnet localhost 143 

    This return connection refused.

    Any idea what should i be looking for?

    • Maybe you can check the Dovecot log.

      sudo journalctl -eu dovecot

      And also the main mail log file. (/var/log/mail.log)

      • Hi,
        I did everything again from scratch and now roundcube is working.
        Guess I made a mistake in some file.

        I have another issue now.
        I can send email just fine from RoundCube but cannot receive.
        I can see in iRedadmin page that email are arriving (check screenshot) but are not reaching roundcube for some reason..

        Any idea?

    • Run the following command on your mail server to see if there are any emails held in the queue.

      sudo postqueue -p

      And check your mail log (/var/log/mail.log or /var/log/maillog) to see if there are any errors in Postfix/Dovecot.
      Emails could be held in the queue if there’s not enough memory on the server.

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