{"id":1779,"date":"2013-02-28T08:22:34","date_gmt":"2013-02-28T08:22:34","guid":{"rendered":"https:\/\/lineo.es\/backup-mx-mailman-exim\/"},"modified":"2026-04-01T07:16:23","modified_gmt":"2026-04-01T07:16:23","slug":"backup-mx","status":"publish","type":"post","link":"https:\/\/lineo.es\/en\/backup-mx\/","title":{"rendered":"Backup MX for Mailman, with Exim"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-post\" data-elementor-id=\"1779\" class=\"elementor elementor-1779\" data-elementor-post-type=\"post\">\n\t\t\t\t<div class=\"elementor-element elementor-element-24e2f63 e-flex e-con-boxed e-con e-parent\" data-id=\"24e2f63\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t<div class=\"elementor-element elementor-element-ed97657 e-con-full e-flex e-con e-child\" data-id=\"ed97657\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-82dae6a elementor-widget elementor-widget-theme-post-title elementor-page-title elementor-widget-heading\" data-id=\"82dae6a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"theme-post-title.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h3 class=\"elementor-heading-title elementor-size-default\">Backup MX for Mailman, with Exim<\/h3>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-c90b79f e-con-full e-flex e-con e-child\" data-id=\"c90b79f\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-9e91680 elementor-widget elementor-widget-text-editor\" data-id=\"9e91680\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>This week a hard disk from one of my servers broke. It has been replaced with just 3 minutes of downtime (kudos to\u00a0<a href=\"http:\/\/www.ovh.com\/\">OVH<\/a>), and right now the RAID array is being rebuilt.<\/p><p>Since this is the first time this happens to me, and the server is running a production mailman service, I decided to take my time and set-up a\u00a0<a href=\"http:\/\/en.wikipedia.org\/wiki\/MX_record#The_backup_MX\">backup MX<\/a>\u00a0server. This is how I did it.<\/p><p>Remember that I am using a Gentoo distribution, this may be slightly different in other distro.<span id=\"more-12\"><\/span><\/p><h4>rsync<\/h4><p>The main problem setting up a backup MX is dealing with Spam. If the backup server accepts a spam message while the main server is down, and then forwards the email to the main server when it is up again, but the main server rejects it because the recipient address does not exist, then what to do? If the backup server bounces to the sending address, and this has been forged, then we are\u00a0<a href=\"http:\/\/en.wikipedia.org\/wiki\/Backscatter_%28email%29\">backscattering<\/a>\u00a0(forwarding spam), and our server will soon be blacklisted.<\/p><p>The point is the backup server should refuse incoming email if the main server would. This means checking the recipient address. What I did is to set-up a cron script to rsync the lists from the main server to the backup server every\u00a0<i>n<\/i>\u00a0minutes.<\/p><p>In the main server I open rsync access for the backup server:<\/p><pre style=\"padding-left: 40px;\">\/etc\/rsyncd.conf\n[mailman]\ncomment = Mailman sync\npath = \/var\/lib\/mailman\nlist = no\nuid = mailman\ngid = mailman\nhosts allow = IP-ADDRESS-OF-THE-BACKUP-SERVER\nhosts deny = *<\/pre><p>This is the script in the backup server:<\/p><pre style=\"padding-left: 40px;\">\/etc\/bin\/mm-sync.sh\n#!\/bin\/sh\n\nSTART=`date`\n\n# data\nrsync -avz --delete MAIN-SERVER::mailman\/data \/var\/lib\/mailman\/ &gt; \/tmp\/mm-sync.log\necho &gt;&gt; \/tmp\/mm-sync.log\n\n# lists\nrsync -avz --delete MAIN-SERVER::mailman\/lists \/var\/lib\/mailman\/ &gt;&gt; \/tmp\/mm-sync.log\necho &gt;&gt; \/tmp\/mm-sync.log\n\n# archives\nrsync -avz --delete MAIN-SERVER::mailman\/archives \/var\/lib\/mailman\/ &gt;&gt; \/tmp\/mm-sync.log\necho &gt;&gt; \/tmp\/mm-sync.log\n\nEND=`date`\n\necho $START &gt;&gt; \/tmp\/mm-sync.log\necho $END &gt;&gt; \/tmp\/mm-sync.log\nmail -s 'Mailman sync' root &lt; \/tmp\/mm-sync.log<\/pre><p>Note that I am syncing three folders:\u00a0<i>data<\/i>,\u00a0<i>lists<\/i>\u00a0and\u00a0<i>archives<\/i>. Actually you only need to sync the\u00a0<i>lists<\/i>\u00a0folder to set-up a backup mx. I sync everything so the backup server can replace the main server completely, if things go really bad.<\/p><p>And this is the cron file:<\/p><pre style=\"padding-left: 40px;\">\/etc\/cron.d\/mm-sync\n*\/10 * * * * mailman \/etc\/bin\/mm-sync.sh<\/pre><h4>Exim<\/h4><p>The exim configuration looks a lot like the configuration of the main server (for instance, I set-up the same blacklist checking rules). See my original post on\u00a0<a href=\"https:\/\/www.j-david.net\/2012\/07\/gentoo-mailman-with-nginx-exim.html\">setting up a Mailman service with exim<\/a>.<\/p><p>First I define the same variables and options as in the main server. Not of them are really needed, but I prefer to keep the configuration in both servers as close possible.<\/p><pre style=\"padding-left: 40px;\">\/etc\/exim\/exim.conf\n# Mailman\nMM_HOME=\/usr\/lib\/mailman\nMM_DATA=\/var\/lib\/mailman\nMM_UID=mailman\nMM_GID=mailman\ndomainlist mm_domains=example.com : example2.com\nMM_WRAP=MM_HOME\/mail\/mailman\nMM_LISTCHK=MM_DATA\/lists\/${lc::$local_part}\/config.pck\n\nsmtp_accept_queue_per_connection = 30<\/pre><p>Now, these mailman domains (<i>mm_domains<\/i>) are not defined as local domains, like in the main server, but as domains we relay to:<\/p><pre style=\"padding-left: 40px;\">domainlist local_domains = @\ndomainlist relay_to_domains = <b>+mm_domains<\/b>\nhostlist   relay_from_hosts = 127.0.0.1 : ::::1<\/pre><p>Now comes the router:<\/p><pre style=\"padding-left: 40px;\"># Mailman\nmailman_router:\n  driver            = <b>dnslookup<\/b>\n  domains           = +mm_domains\n  require_files     = MM_LISTCHK\n  local_part_suffix_optional\n  local_part_suffix = -admin     : \\\n         -bounces   : -bounces+* : \\\n         -confirm   : -confirm+* : \\\n         -join      : -leave     : \\\n         -owner     : -request   : \\\n         -subscribe : -unsubscribe\n  transport         = <b>remote_smtp<\/b>\n<b>no_more<\/b><\/pre><p>This is very much like the router used in the main server. The differences are: use the\u00a0<i>dnslookup\u00a0<\/i>driver instead of\u00a0<i>accept<\/i>\u00a0; use the\u00a0<i>remote_smtp<\/i>\u00a0transport ; and add the\u00a0<i>no_more<\/i>\u00a0option. In other words, configure the router for remote delivery instead of local delivery.<\/p><p>Last, we need to modify the\u00a0<i>dnslookup<\/i>\u00a0router to not consider the mailman domains, since we have already handled them in the mailman router:<\/p><pre style=\"padding-left: 40px;\"> dnslookup:\n   driver = dnslookup\n   domains = ! +local_domains <b>: ! +mm_domains<\/b>\n   transport = remote_smtp\n   ignore_target_hosts = 0.0.0.0 : 127.0.0.0\/8\n   no_more<\/pre><p>You should test the router for both valid and invalid recipient addresses:<\/p><pre style=\"padding-left: 40px;\"># exim -bt foobar@example.com\n  router = mailman_router, transport = remote_smtp\n  host example.com [AAA.BBB.CCC.DDD] MX=10\n# exim -bt foobarrr@example.com\nfoobarrr@example.com is undeliverable: Unknown user<\/pre><h4>The DNS<\/h4><p>Now remember to add your backup MX server to the DNS configuration. This is what it looks like mine:<\/p><pre style=\"padding-left: 40px;\">$ host example.com\nexample.com has address AAA.BBB.CCC.DDD\nexample.com mail is handled by 10 smtp.example.com.\nexample.com mail is handled by 20 mx.example.com.<\/pre><p>Note that the lowest the number, the highest the priority. This is a little confusing at first.<\/p><h4>Testing<\/h4><p>Now, how do you test this? There is a wonderful utility for testing email servers,\u00a0<a href=\"http:\/\/www.jetmore.org\/john\/code\/swaks\/\">swaks<\/a>. I did the tests from my workstation:<\/p><pre style=\"padding-left: 40px;\">localhost ~ $ sudo emerge swaks<\/pre><p>While doing the tests, open a console on every server and watch the exim log (<tt>tail -f \/var\/log\/exim\/exim_main.log<\/tt>). First test is to check the backup server forwards emails to the main server:<\/p><pre style=\"padding-left: 40px;\">localhost ~ $ swaks --to foobar@example.com --from toto@gmail.com --server mx.example.com<\/pre><p>Second test. Simulate a downtime of the main server. To do this I use iptables in the main server to block connections from the backup server to the SMTP port:<\/p><pre style=\"padding-left: 40px;\">smtp.example.com ~ # iptables -A INPUT -s BACKUP-SERVER-IP -p tcp --destination-port 25 -j DROP\nlocalhost ~ $ swaks --to foobar@example.com --from toto@gmail.com --server mx.example.com<\/pre><p>Wait a few minutes, and then open access again:<\/p><pre style=\"padding-left: 40px;\">smtp.example.com ~ # iptables -D INPUT -s BACKUP-SERVER-IP -p tcp --destination-port 25 -j DROP<\/pre><p>That\u2019s all folks! Something wasn\u2019t clear? Please drop a comment.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>This week a hard disk from one of my servers broke. It has been replaced with just 3 minutes of downtime (kudos to\u00a0OVH), and right now the RAID array is being rebuilt. Since this is the first time this happens to me, and the server is running a production mailman service, I decided to take [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"elementor_header_footer","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1779","post","type-post","status-publish","format-standard","hentry","category-sin-categoria"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/lineo.es\/en\/wp-json\/wp\/v2\/posts\/1779","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lineo.es\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/lineo.es\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/lineo.es\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lineo.es\/en\/wp-json\/wp\/v2\/comments?post=1779"}],"version-history":[{"count":6,"href":"https:\/\/lineo.es\/en\/wp-json\/wp\/v2\/posts\/1779\/revisions"}],"predecessor-version":[{"id":2140,"href":"https:\/\/lineo.es\/en\/wp-json\/wp\/v2\/posts\/1779\/revisions\/2140"}],"wp:attachment":[{"href":"https:\/\/lineo.es\/en\/wp-json\/wp\/v2\/media?parent=1779"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/lineo.es\/en\/wp-json\/wp\/v2\/categories?post=1779"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/lineo.es\/en\/wp-json\/wp\/v2\/tags?post=1779"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}