Latest version: 1.4.6

README.exim4 May 7th 2005

This is a step-by-step guide to run mlmmj with Exim4. The most current version of this can be found on

Notes: - We assume that you have a user and group called mlmmj to use with mlmmj - The exim user needs rx access rights to mlmmj’s spool directory. (If you don’t want that, see below.) The easiest way is “chmod 755 /path/to/mlmmj/spool”, if it’s ok that local users can see which lists there are. Note that the owner of the mlmmj spool must still be the mlmmj user (and this user must have at least x rights to the directories below). - Existence of mailing lists is automatically checked ($listdir) and you don’t need to put anything into your aliases file - If you want VERP to be done by your MTA, follow the instructions below and put an empty file named verp into the control directory of your lists

  1. In the main configuration section:
domainlist mlmmj_domains =
  1. Add +mlmmj_domains to relay_to_domains:
domainlist relay_to_domains = other.domain : +mlmmj_domains
  1. mlmmj is barely interested in delay warnings, so add this in the main configuration:
delay_warning_condition = ${if match_domain{$domain}{+mlmmj_domains}{no}{yes}}
  1. In the routers section (before the dnslookup router, preferably at the beginning):
  driver = accept
  domains = +mlmmj_domains
  require_files = MLMMJ_HOME/${lc::$local_part}
  # Use this instead, if you don't want to give Exim rx rights to mlmmj spool.
  # Exim will then spawn a new process running under the UID of "mlmmj".
  #require_files = mlmmj:MLMMJ_HOME/${lc::$local_part}
  local_part_suffix = +*
  headers_remove = Delivered-To
  headers_add = Delivered-To: $local_part$local_part_suffix@$domain
  transport = mlmmj_transport

If you want VERP to be done by your MTA, also add this:

  driver = dnslookup
  domains = !+mlmmj_domains
  # we only consider messages sent in through loopback
  condition = ${if eq{$sender_host_address}{}{yes}{no}}
  ignore_target_hosts = <;;; ::1/128; fe80::/10; ff00::/8
  # only the un-VERPed bounce addresses are handled
  senders = \N^.+\+bounces-\d+@.+\N
  transport = verp_smtp

To prevent temporary errors for not-existing lists, add !+mlmmj_domains to the domains condition of the dnslookup router:

  driver = dnslookup
  domains = !+mlmmj_domains : !+local_domains
  1. Somewhere in the transports section. (Change the path of mlmmj-receive if you don’t use the default location!):
  driver = pipe
  user = mlmmj
  group = mlmmj
  home_directory = MLMMJ_HOME
  current_directory = MLMMJ_HOME
  command = /usr/local/bin/mlmmj-receive -F -L MLMMJ_HOME/${lc:$local_part}

If you want VERP to be done by your MTA, also add this:

  driver = smtp
  # put recipient address into return_path
  return_path = ${quote_local_part:${local_part:$return_path}}-\
  # must restrict to one recipient at a time
  max_rcpt = 1
  # Errors-To: may carry old return_path
  headers_remove = Errors-To
  headers_add = Errors-To: $return_path
  1. Test your setup with
$ exim -bt mlmmj-test@your.list.domain
  router = mlmmj_router, transport = mlmmj_transport

If you get different output, run it with -d to see what’s going wrong. If not, you’re done!

Jakob Hirsch (jh at plonk dot de)