Secondary MX System

We have seen many (oldish) discussions about this topic. Mostly (at least what we found) incomplete and dated…

To CLARIFY, this is about a ‘redundant’ or ‘secondary’ MX server for your eMail which will be listed in your DNS record. It will accept eMail for the domain when the primary is down or busy/slow to respond. The secondary will deliver anything received to the primary once it is available. This is NOT a solution for ‘clustering’ eMail servers wherein your users can communicate with multiple servers! That means this does NOT prevent your users from getting a ‘server down’ response! This will ONLY serve as an alternative to other MTAs to avoid ‘undeliverable/failed/bounce’ message to THEIR senders’.


The content of this article is based on a HESTIA CP installation running on top of Ubuntu 20.04 (most likely working fine with VestaCP and 18.04, 16.04 and DEBIAN too. For other OS, if you still trust in Centos, the paths for system files can be adapted from the VESTA support info. At the time of writing HESTIA is only installed/supported on DEBIAN/UBUNTU). Of course: YMMV!

For VESTA you must replace the path variable “$HESTIA” with “$VESTA” in the bash script.

It is expected that:

  • DNS (bind9) is installed/running
  • Your DNS is pointing to your vanity/child DNS server
  • You are running the exim4 mailserver for one or more domains
  • You are running a second server (VPS) which is likely on another subnet
  • Ideally your second server is located in a differnt data centre
  • MY typical setup is a group of 2+ VPSs that also serve as DNS cluster!
The FIRST step is simple
While it is the simpler part, this step ‘should’ be done together with step 2… (it is only listed first because you may not want/like/need the second step 😉
  1. On your SECONDARY MX server!
  2. EDIT your exim4 configuration
    nano /etc/exim4/exim4.conf.template
  3. FIND the code for relays
    domainlist relay_to_domains = dsearch;/etc/exim4/domains/
  4. ADD to the end of the line (path and filename can be chosen freely)
    domainlist relay_to_domains = dsearch;/etc/exim4/domains/ : lsearch;/etc/exim4/relay_domains_all
  5. Save the template… (NB: it is NOT yet active/enabled!)
  6. CREATE the file you referenced above and
    ADD one line for EACH domain that you will relay for…(e.g. domiain.tld)
    nano /etc/exim4/relay_domains_all
  7.  Save the file and update exim4 configuration followed by a restart of exim4
    (the file needed to exist to avoid errors in the paniclog!)
    update-exim4.conf && systemctl restart exim4
  8. Now you can test that your secondary DOES accept mail for the new (remote) domain(s) you have added. Try any service OR telnet into your secondary. One option is the free SMTP Test at Wormly monitoring. Use the IP or mail.hostname.tld of your SECONDARY and send to any address at your primary domain. This should delivery to your primary inbox after a short delay.
  9. IF all is fine simply ADD an MX record on your primary which points to your secondary domain and has a HIGHER priority number (= used LATER). There is a sample modified template below which you can edit to include YOUR secondary domain(s) and save to the $HESTIA/data/templates/DNS/ folder. THEN it can be used for DNS… THIS avoids HESTIA “breaking” your DNS on updates… NB: In MY configuration this is enough because the primary (and secondary) are in a redundant DNS cluster = they know each other’s IP resolution. YMMV!
The RUB!
  1. NOW that you have done is THE RUB – and the suggested solution.

ALL is “good”? When your primary MX goes down or is busy/slow other MTA will delivery to the secondary(s). That was what we wanted. HOWEVER, in my configurations I DO NOT allow ‘catch-all’ type of addresses and in fact the default configuration will check RCPT (the recipient MUST be valid) and bounce for any recipients (and aliases) we don’t have. In this configuration your SECONDARY does NOT know the valid recipients of the primary! Thus ALL recipients are accepted from the foreign MTA. BUT, when SECONDARY tries to hand off received mail to the primary any that are not valid get bounced. NOW your queue at the secondary fills up with SPAM and spammers deliberately use secondary MX record to try and backdoor “their way IN”!


  1. Check the queue on secondary, send bounce messages back to originator… (Assuming they are spammers they don’t care and likely their return path will bounce… YIKES! and likely NOT RFC compliant!)
  2. Validate recipients using a common/shared LDPA… IF you have it… may be…
  3. My choice below…
  4. You may have a better mousetrap…

This method uses specific HESTIA (VESTA) binaries (scripts) because they exist and can provided what we want…
We make extensive use of sed (the stream editor) and it may “behave” differently on non-Debian/Ubuntu servers (YMMV)!

Our script will do the following (run the cron as frequently as needed – how often do you add recipients?):

  1.  run HESTIA script to find users with “mail domains”
  2. find the domains the user has WITH mail
  3. query each USER-Domain combo for valid recipients (recipient + ALL aliases )
  4. Create TWO files one with the mail-domains and one with ALL valid recipients (local@domain.tld).
  5. The file are named for the primary
  6. The files are copied to a nginx location which is configured to ONLY accept the secondary IP for listing (i.e. ALL other ‘visitors’ to this data are DENIED! = prevent harvesting of valid mail addresses!)
  7. The secondary runs a cron (ideally 6-12minutes AFTER the above) to:
    • wget the files from all primary URLs
    • compound the domains and recipients from all primaries to one file EACH
    • place the two file where exim4 is expecting them
    • restart exim4 to pickup the new/updated files
      (UPDATE:This is NOT needed, exim4 reads the files for each time ACL is invoked…)
  8. To make this part work we need to edit the configuration template on the SECONDARY one more time (NB: since I use a group of 3 VPSs to act as both… ALL my exim4.conf are like this – it DOES NOT interfere with the core/primary funtion)
    nano /etc/exim4/exim4.conf.template
  9. FIND the section in ACL that has the following line (there should only be ONE)
      require message       = relay not permitted
  10. ADD the following code block BEFORE that line
    # ------------------------------------------------------------------------------------------------------
    # For OUR secondary MX check valid recipients ONLY to avoid bounce/freeze upon delivery to primary MX 
    # SCRIPT to maintain valid list of recipients from primary MX... must write to THIS file
    # MUST meet all conditions to deny = this deny fails on 1 if it goes to local = not a relay
    # ------------------------------------------------------------------------------------------------------
      deny    message       = Recipient UNKNOWN at secondary MX - try primary
              !domains      = +local_domains
              domains       = +relay_to_domains
              !recipients   = /etc/exim4/relay_recipients_all
    # ------------------------------------------------------------------------------------------------------
  11. SAVE the modified template! AND CREATE the whitelist file.
    ADD a single line for EACH recipient that is valid  ( ! MUST be:  local@domain.tld ).
    (NB: the /path/to/file/relay_recipients_all “can be chosen freely” as long as you use it everywhere.)

      nano /etc/exim4/relay_recipients_all
  12. SAVE the whitelist and NOW update exim4 conf and restart exim4
    (Same as before, the file has to exist before we change the exim4 configuration!)
      update-exim4.conf && systemctl restart exim4
  13. NOW you can TEST SMTP again an you will find that recipients are ONLY accepted if the are “in the list”. Later our script will update these files and automatically add new domains and recipients…
  14. INSTALL the SECONDARY bash script below to your secondary server(s), edit the URL and filenames of the primary, execute. IF all is well the correct domain and recipient list have been copied to exim4 and ARE in USE. Once it works correctly you may add this to a cron job. (one cron on the primary to create the list AND on cron on the SECONDARY to fetch and install… 3-12 minutes later…)
  15. CAVEAT:
    – recipients deleted at the primary will be accepted at the secondary until the next update cycle runs (cron)
    – recipients added at the primary will be accepted at the secondary until the next update cycle runs (cron)
  16. Possible IMPROVEMENTS:
    – on primary, trigger an update cycle when HESTIA runs add/delete recipient (OR ALIAS) this could eliminate the need for a cron entirely…
    – on secondary receive an API trigger when there is an update on primary…
  17. Other ideas? Leave a comment…
  18. OOOPS, almost forgot the scripts… lol

We WELCOME constructive comments and discussion! However, due to rampant abuse and comment SPAM, we only allow comments by verified registered users. Please take a minute to register with your eMail address (valid & verifiable ). We DO NOT send SPAM or sell/use/market your information! PERIOD!

Unless mentioned otherwise within, these pages, images and code are open source and licensed under creative commons (with attribution and share alike) version 4.0 or later. Where suitable we may have incorporated and credited other open source text and/or images.

Our preference is for FOSS (Free Open Source Software) wherever and whenever possible. It is NOT necessarily unpaid ;-) Please understand the ideas and principals behind FOSS and DO donate or contribute as and when you can.

Rev.: B.20210301