From Wikitech
This page may be outdated or contain incorrect details. Please update it if you can.

Needs to be updated for Mailman3

Near the top of the exim4.conf file, there are several macros related to Mailman. These define system-specific settings/locations used by the router(s) and transport(s) in the rest of the configuration file. For a Debian/Ubuntu Mailman package, the following macro's are accurate:

# Mailman
MAILMAN_HOME = /usr/lib/mailman
MAILMAN_LISTS_HOME = /var/lib/mailman

There's a domain list that contains a list of all domains that can "contain" mailing lists, i.e. the domains for which the Mailman router(s) should run. This list is also used as part of the "local domains" list, the list for which this mail server accepts mail and handles it locally.

domainlist mailman_domains =
domainlist local_domains = +system_domains : +mailman_domains

Main configuration

Several tweaks have been made to the main configuration to make Mailman delivery go smooth.

In case of high load / lots of incoming connections, mail from the local host (including Mailman) and other Wikimedia servers are given preference:

smtp_reserve_hosts = <; ; ::1 ; +wikimedia_nets

For big mailing lists, Mailman needs to send a lot of recipients per mail / connection. Per default, Exim only queues mails that have > 10 recipients, to be delivered by a subsequent queue runner, which can cause significant delays. The default Mailman limit is 500 recipients per connection, so make Exim accept that:

smtp_accept_queue_per_connection = 500

Allow Exim to do 50 deliveries to remote hosts in parallel (this means 50 processes):

remote_max_parallel = 50


In Exim, the routers determine if a certain e-mail address is accepted for delivery or mail transport, and how it's going to be handled (routed). For Mailman, the following list router accepts a recipient that:

  • has a domain in the domain list mailman_domains
  • has a Mailman configuration file matching the local part (i.e. the mailing list exists)

Certain postfixes of the localpart, e.g. "-bounces" are accepted as well.

When the router accepts the recipient address, it's set up for delivery using the list transport (see below).

# Mailman list handling. Test the mailing list address without suffix
# first, as a mailing list like wikifi-admin is a valid list name.

        driver = accept
        domains = +mailman_domains
        require_files = MAILMAN_LISTS_HOME/lists/$local_part/config.pck
        transport = list

        driver = accept
        domains = +mailman_domains
        require_files = MAILMAN_LISTS_HOME/lists/$local_part/config.pck
        local_part_suffix = -bounces : -bounces+* : \
                                -confirm+* : -join : -leave : \
                                -owner : -request : -admin : \
                                -subscribe : -unsubscribe
        transport = list

If the conditions for this router fail (i.e. the router is not run) then the no_more makes sure that no subsequent routers will be tried (in the current configuration there are none that might accept), and the recipient address is failed.


An Exim transport configures a way of transporting a message, e.g. over the network (SMTP), to a file (MBOX/Maildir/etc) or using a pipe to a process. The following transport sets up delivery to Mailman:

# Mailman pipe transport

        driver = pipe
        command = MAILMAN_WRAP \
                '${if def:local_part_suffix \
                        {${sg{$local_part_suffix}{-(\\w+)(\\+.*)?}{\$1}}} \
                        {post}}' \
        current_directory = MAILMAN_LISTS_HOME
        home_directory = MAILMAN_LISTS_HOME
        user = MAILMAN_UID
        group = MAILMAN_GID

For content scanning, temporary mbox files are written to /var/spool/exim4/scan, and deleted after scanning. Similarly, Exim keeps "hints" databases in /var/spool/exim4/db, which are non-essential caches. To improve performance somewhat, these directory is mounted as a tmpfs filesystem, using the following line in /etc/fstab:

tmpfs   /var/spool/exim4/scan   tmpfs   defaults        0       0
tmpfs   /var/spool/exim4/db     tmpfs   defaults        0       0

Mailing list privacy protection

It has happened in the past that by hitting on the Reply All button in one's e-mail client, private info from an internal list leaked to a public mailing list because it was listed in the CC list, and the imprudent sender did not notice. In order to try to catch these incidents, a little filter has been implemented.

  1. If the To: or CC: of a message body matches an item in a list of private mailing list addresses, and
  2. the list of recipients as known by the mailing list server contains a Wikimedia mailing list that's not a private mailing lists, then
  3. the message is bounced with the message Message rejected for privacy protection: The list of recipients contains both private and public lists.

It's possible to circumvent this restriction by sending to the private list as a BCC, Blind Carbon Copy.

This filter is implemented using an Exim system filter:

# Exim filter # Mailing list privacy protection if foranyaddress $h_To:,$h_Cc: ( $thisaddress matches "\\N^(internal-l|private-l)@(lists\.|mail\.)?wiki[mp]edia\.org$\\N" ) then if foranyaddress $recipients ( $thisaddress matches "\\N@lists\.wikimedia\.org$\\N" and $thisaddress does not match "\\N^(internal-l|private-l)@\\N" ) then fail text "Message rejected for privacy protection: The list of recipients contains both private and public mailing lists" endif endif

This filter is enabled in the configuration file using

system_filter = CONFDIR/system_filter

Address header rewriting

It turned out that, after the migration, many users kept sending mails to both the old and the new mailing list addresses, thereby causing duplicate messages. To reduce this, Exim has been configured to rewrite the old mailing list addresses to the new ones in the To: and CC: headers, using the following option on the list transport:

list: ... # Rewrite body headers of old mailing list addresses to new ones headers_rewrite = \N^.*@(mail\.)?wiki[mp]edia\.org$\N "${if exists{MAILMAN_LISTS_HOME/lists/$local_part/config.pck}{$}fail}" ct