I'm using Mailu for my personal domains. It's a collection of docker containers that provides a full mail package with SMTP, IMAP, Webmail and a web-based admin frontend.

For coreboot.org we needed some alternative to its home-grown 15 years old mail server setup, and naturally I looked into using Mailu for that purpose. The only complication is that coreboot.org was using mailman for mailing list management.

Mailman 2 (as used until December) isn't very suitable when trying to use it with a mail server on a different system (or docker container as it were with Mailu), so I also checked out converting the lists to Mailman 3, for which there are great docker containers: I only needed to combine both sets of containers in a single coherent package. This article documents how to do that.

The first step is to install Mailu per its documentation. I'm using 1.5 but with some local changes that made it into Mailu's master branch.

Once that works, set up a dedicated network in the Mailu docker-compose.yml:

networks:
  default:
    driver: bridge
    ipam:
      config:
        - subnet: 172.30.0.0/24

Next, add the mailman containers:

  mailman-core:
    image: maxking/mailman-core:latest
    restart: always
    env_file: .env
    depends_on:
      - database
    volumes:
      - "$ROOT/mailman-core:/opt/mailman/"
    environment:
      - HYPERKITTY_API_KEY=$SOME_SECRET_KEY
      - DATABASE_URL=postgres://someuser:somepassword@database/mailmandb
      - DATABASE_TYPE=postgres
      - DATABASE_CLASS=mailman.database.postgresql.PostgreSQLDatabase
      - MTA=postfix
    networks:
      default:
        ipv4_address: 172.30.0.99

  mailman-web:
    image: maxking/mailman-web:latest
    restart: always
    env_file: .env
    links:
      - mailman-core:mailman-core
    depends_on:
      - database
      - mailman-core
    volumes:
      - "$ROOT/mailman-web:/opt/mailman-web-data/"
    environment:
      - SERVE_FROM_DOMAIN=mail.coreboot.org
      - HYPERKITTY_API_KEY=$SOME_SECRET_KEY
      - SECRET_KEY=$ANOTHER_SECRET_KEY
      - DATABASE_TYPE=postgres
      - DATABASE_URL=postgres://someuser:somepassword@database/mailmandb
      - SMTP_HOST=smtp
      - MAILMAN_HOST_IP=172.30.0.99

  database:
    image: postgres:9.6-alpine 
    restart: always
    environment:
      POSTGRES_DB: mailmandb
      POSTGRES_USER: someuser
      POSTGRES_PASSWORD: somepassword
    volumes:
      - "$ROOT/mailman-db:/var/lib/postgresql/data"

In addition, some of mailman's directories need to be exposed to Mailu so that its SMTP server knows about the mailing lists and the web server about Mailu's static web files:

front:
  …
  volumes:
    …
    - "$ROOT/overrides/nginx:/overrides"
    - "$ROOT/mailman-web:/opt/mailman-web-data/"
    - "$ROOT/static-web:/opt/static-web/"
  depends_on:
    - mailman-web

smtp:
  …
  volumes:
    …
    - "$ROOT/mailman-core/var/data:/opt/mailman-core-data/"

As there's a conflict between the two services over the /admin path, reroute Mailu's admin interface to another path in mailu's .env file:

# Path to the admin interface if enabled
WEB_ADMIN=/mailu-admin

Finally, some Mailu configuration needs overrides to tell it what mails or http requests need to be forwarded to mailman. For this purpose, add two files in Mailu's $ROOT directory. First, $ROOT/overrides/nginx/mailman-uwsgi.conf:

location /static {
        root /opt/mailman-web-data;
}

location / {
        uwsgi_pass mailman-web:8080;
        include uwsgi_params;
        uwsgi_read_timeout 300;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
}

Next, $ROOT/overrides/postfix.cf:

append_at_myorigin=no
append_dot_mydomain=no
recipient_delimiter = +
unknown_local_recipient_reject_code = 550
owner_request_special = no
virtual_mailbox_maps = regexp:/opt/mailman-core-data/postfix_lmtp \$virtual_alias_maps
virtual_mailbox_domains = \${sql}sqlite-virtual_mailbox_domains.cf
transport_maps = regexp:/opt/mailman-core-data/postfix_lmtp \${sql}sqlite-transport.cf
local_recipient_maps = regexp:/opt/mailman-core-data/postfix_lmtp
always_add_missing_headers = yes
local_header_rewrite_clients = permit_sasl_authenticated

With all this, mailman 3 should be functional as part of the Mailu config. The root URL on your domain will point to Mailman's postorious, the Mailu services are available in the configured subdirectories.

I recommend configuring the mailman 3 container to use Xapian for full text indexing (which currently requires a few changes to the containers) if your lists have any considerable archives already: The default Whoosh indexer failed miserably for our 4GB of mailing list history, driving up the system's load to obscenely high levels for days without showing a sign of slowing down.