Mailman is free software for managing electronic mail discussion and e-newsletter lists. Mailman is integrated with the web, making it easy for users to manage their accounts and for list owners to administer their lists. Mailman supports built-in archiving, automatic bounce processing, content filtering, digest delivery, spam filters, and more.
Mailman is free software, distributed under the GNU General Public License, and written in the Python programming language.
Index
- Preparing the virtual environment
- Installing
Mailman core
- Set up
Mailman Web
UI
- Log rotation
- Adding domains
- Known issues
If you are looking for a more complete and advanced mailing list software than ezmlm
, Mailman
is definitely one of the best options.
It seems that there are many different guides even in the official documentation and it's not easy to find the best starting point at the beginning.
This is the one that worked for me (I'll add further links to the documentation during the next steps):
Preparing the virtual environment
- More info here
The
venv
module supports creating lightweight “virtual environments”, each with their own independent set of Python packages installed in theirsite
directories. A virtual environment is created on top of an existing Python installation, known as the virtual environment’s “base” Python, and may optionally be isolated from the packages in the base environment, so only those explicitly installed in the virtual environment are available.
Add the mailman user:
groupadd mailman useradd -g mailman -d /usr/local/mailman mailman mkdir /usr/local/mailman chown -R mailman:mailman /usr/local/mailman
The mailman user's home directory /usr/local/mailman is the container where the virtual environment that we are going to build will live.
The creation of the virtual environment must be done as the mailman user:
su - mailman
First of all create the .profile file for the mailman user, so that the next time you call the pip
binary it will be taken from its virtual environment (run the same from the command line or exit from the shell):
echo "export PATH=~/bin:\$PATH" > .profile
Prepare the virtual environment:
python3 -m venv --upgrade-deps /usr/local/mailman
This will create the virtual environment in /usr/local/mailman. Always as the mailman user activate the virtual environment:
cd source bin/activate
Your command prompt will slightly change its appearence to inform that you entered the virtual environment.
(mailman) mailman@qmail:~$
Activating the virtual environment will prepend the /usr/local/mailman directory to your PATH
, so that running python
will invoke the environment’s python
interpreter and you can run installed scripts without having to use their full path.
Be aware that you have to activate the virtual environment each time you do an (un)installation/upgrade of a python
package. To deactivate just run the deactivate
command.
The rest of this documentation mostly assumes that the virtual environment is activated. Whether or not a certain command needs that the virtual environment is activated can be seen by a (mailman)
before the $
shell prompt. It is also assumed that you activate the virtual environment as the mailman user.
Installing Mailman core
- Version: GNU Mailman 3.3.9 (Tom Sawyer)
- Official docs
Do not exit from the virtual environment as we have to do the Mailman core
installation from the inside using the pip
command, which is a copy of the pip3
in our virtual environment.
(mailman)$ pip install mailman
This command installs Mailman
core
with all its prerequisites in the virtual environment. If everything is ok it exits with a "Successfully installed" verb followed by the list of the packages installed.
Typing mailman info
will show the details and version of the installed Mailman.
Configuration
By default Mailman
will look for its configuration file in the ~/var/etc directory. Since I prefer to use ~/etc (also for other config files that have to come later), some adjustment is needed. We have to define the environment variable MAILMAN_CONFIG_FILE
in the .profile file:
su - mailman source bin/activate (mailman)$ cat >> ~/.profile << __EOF__ export MAILMAN_CONFIG_FILE=~/etc/mailman.cfg export PYTHONPATH=~/etc export PYTHONHOME=~/ __EOF__
Create the etc directory
(mailman)$ mkdir -p ~/etc
Now edit the ~/etc/mailman.cfg, which overwrites the settings shown in schema.cfg.
(mailman)$ cat >> ~/etc/mailman.cfg << __EOF__ [devmode] enabled: no [mailman] # This address is the "site owner" address. Certain messages which must be # delivered to a human, but which can't be delivered to a list owner (e.g. a # bounce from a list owner), will be sent to this address. It should point to # a human. site_owner: postmaster@mydomain.tld # The default language for this server. default_language: en [paths.master] var_dir: /usr/local/mailman/var [logging.database] level: warn [logging.debug] path: debug.log level: warn [logging.http] level: warn [logging.smtp] path: smtp.log level: info [language.it] description: Italian charset: utf-8 enabled: yes [mta] # https://docs.mailman3.org/projects/mailman/en/latest/src/mailman/docs/mta.html#qmail # # NullMTA is just implementing the interface and thus satisfying Mailman # without doing anything fancy incoming: mailman.mta.null.NullMTA # Mailman should not be run as root. # Use any convenient port > 1024. 8024 is a convention, but can be # changed if there is a conflict with other software using that port. lmtp_host: 127.0.0.1 lmtp_port: 8024 # This will listen on localhost:8024 with LMTP and deliver outgoing messages to localhost:25. # How to connect to the outgoing MTA. If smtp_user and smtp_pass is given, # then Mailman will attempt to log into the MTA when making a new connection. #smtp_host: 10.0.0.4 # Some list posts and mail to the -owner address may contain DomainKey or # DomainKeys Identified Mail (DKIM) signature headers <http://www.dkim.org/>. # Various list transformations to the message such as adding a list header or # footer or scrubbing attachments or even reply-to munging can break these # signatures. It is generally felt that these signatures have value, even if # broken and even if the outgoing message is resigned. However, some sites # may wish to remove these headers by setting this to 'yes'. remove_dkim_headers: no __EOF__
Adjust the preferred language and the site owner's email to your needs.
Secure mailman.cfg as it has to store the database password:
chmod o-r ~/etc/mailman.cfg
Hooking up qmail
to work with Mailman
The following commands must be run as root, so exit from the mailman shell (exit
).
We'll host the lists on a virtual domain, let's say lists.mydomain.tld (if you want to use other domains as well or ordinary virtual domains for your lists read below towards the end of this article), and use the /var/qmail/control/virtualdomains file to put the mailman user in charge of this virtual domain:
echo lists.mydomain.tld:mailman >> /var/qmail/control/virtualdomains
Be aware that this virtual domain should not be created by the usual vadddomain
program, as it exists just to bind the mailman user to the lists.mydomain.tld domain and its definition is different from the vpopmail
's ordinary domains. In this context, mailman is a qmail/vpopmail
user which has nothing to do with its counterpart in the Linux
user space.
Make also sure to setup SPF
, DKIM
and DMARC
for lists.mydomain.tld.
Add lists.mydomain.tld to rcpthosts so that qmail-smtpd
will accept mails for that domain. Do not add it to control/locals otherwise the virtualdomains file will be ignored and the Mailman
program won't be called.
echo lists.mydomain.tld >> /var/qmail/control/rcpthosts
The settings to make Mailman
work with qmail
are already in the mailman.cfg that we created above:
[mta] # NullMTA is just implementing the interface and thus satisfying Mailman # without doing anything fancy. Default would be postfix otherwise. incoming: mailman.mta.null.NullMTA # Use any convenient port > 1024. 8024 is a convention, but can be # changed if there is a conflict with other software using that port. # This will listen on localhost:8024 with LMTP and deliver outgoing messages # to localhost:25. lmtp_port: 8024
The qmail-lmtp
script in the contrib directory is used to tell Mailman
how many parts (separated by dashes) of the destination address to filter out. We'll not use the one available in the contrib directory because it doesn't work with mailman3
, as explained here, where I found a patch to cure the problem. In fact, qmail-local
sends bare LF for the end line, while the SMTP
standards require CRLF, so the SMTP
conversation between qmail
and Mailman
ends up with the error: "Line too long (see RFC5321 4.5.3.1.6)". According to what a developer said the patched qmail-lmtp
file that we are going to install can have unpredictable results when binary files with \n and \r characters are sent (that would need qmail-local
to be patched). Anyway it works fine here with my lists, although I've never tested them with binary files.
(mailman)$ wget -O ~/qmail-lmtp https://notes.sagredo.eu/files/qmail/mailman/qmail-lmtp
Now insert the call to that script in the default dot-qmail
file
(mailman)$ echo "|~/qmail-lmtp 8024 1" > ~/.qmail-default
The first argument specifies the LMTP
port of Mailman
.
Since inbound messages are delivered by vpopmail
to the user mailman, it's necessary to allow it to access its home directory ~mailman. In particular vpopmail
must have read access to the ~/mailman/.qmail-default file, which stores the informations about the program that will handle the delivery, Mailman
in our case:
(mailman)$ chmod 644 ~/.qmail-default
Mailman
will send via
LMTP
to our qmail MTA
tons of concurrent mails and if you enabled a limit on the number of recipients specified per message, you need to change that limit to accomodate the number of recipients of your lists. If you are using the qmail-maxrcpt
patch that is built in my qmail
package, check the limit in the qmail/control/maxrcpt file. You can set DISABLE_MAXRCPT
in your tcprules
to disable the limit for local IPs
(v. 2024.06.08 upwards).
Finally we have to avoid to run simscan
's filters when qmail
receives messages from the Mailman
's LMTP
service, as it may throw it into DKIM
verification errors. Modify your tcp
rules accordingly adding a QMAILQUEUE="/var/qmail/bin/qmail-queue"
rule to your local IP
s, for example:
127.:allow,RELAYCLIENT="",DISABLE_MAXRCPT="",QMAILQUEUE="/var/qmail/bin/qmail-queue" 0.0.0.0:allow,RELAYCLIENT="",DISABLE_MAXRCPT="",QMAILQUEUE="/var/qmail/bin/qmail-queue" 10.0.0.:allow,RELAYCLIENT="",QMAILQUEUE="/var/qmail/bin/qmail-queue"
Then compile the tcp rules with qmailctl cdb
.
Setting up MySQL
Mailman
needs a relational database to provide persistence of data. If you prefer to store the data to SQLite
proceed to the next step, as it is the default database. To use MySQL
, install the pymysql
module first:
(mailman)$ pip install pymysql
Then prepare the database and the MySQL
mailman user:
CREATE USER 'mailman'@'localhost' IDENTIFIED VIA mysql_native_password USING '***'; GRANT USAGE ON *.* TO 'mailman'@'localhost' REQUIRE NONE WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0; CREATE DATABASE IF NOT EXISTS `mailman` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; GRANT ALL PRIVILEGES ON `mailman`.* TO 'mailman'@'localhost';
Of course change localhost if the MySQL
server is elsewhere in your net.
Add the database settings in the ~/etc/mailman.cfg file
(mailman)$ cat >> ~/etc/mailman.cfg << __EOF__ [database] class: mailman.database.mysql.MySQLDatabase url: mysql+pymysql://mailman:password@localhost/mailman?charset=utf8mb4&use_unicode=1 __EOF__
Running Mailman
To start
/stop
the server you can do mailman start/stop
as the mailman user. If your system has systemd
please refer to the official documentation here, otherwise download the init script, make it executable and load it into your rc.local
.
This has to be done as root
wget -O /usr/local/bin/mmctl https://notes.sagredo.eu/files/qmail/mailman/mmctl chmod +x /usr/local/bin/mmctl mmctl start
The startup script needs to change to the ~mailman dir due to a bug.
If the mmctl info
command returns no error Mailman core
is configured successfully:
# mmctl info GNU Mailman 3.3.9 (Tom Sawyer) Python 3.9.19 (main, Mar 20 2024, 14:44:21) [GCC 11.2.0] config file: /usr/local/mailman/var/etc/mailman.cfg db url: mysql+pymysql://mailman:password@localhost/mailman?charset=utf8mb4&use_unicode=1 devmode: DISABLED REST root url: http://localhost:8001/3.1/ REST credentials: restadmin:restpass
You can do the same test as the mailman user in activated mode calling mailman info
.
You can interact with Mailman
via command line by typing mailman shell
(help()
for interactive help), but it will be easier to play with the Postorius
web interface.
Cronjobs
Mailman
requires the following cronjobs for periodic actions. Add the following to the mailman user cronjob (su - mailman && crontab -e
):
@daily ID=mailman source /usr/local/mailman/bin/activate && /usr/local/mailman/bin/mailman -C /usr/local/mailman/etc/mailman.cfg notify >> /usr/local/mailman/var/logs/cron.log 2>&1 @daily ID=mailman source /usr/local/mailman/bin/activate && /usr/local/mailman/bin/mailman -C /usr/local/mailman/etc/mailman.cfg digests --periodic >> /usr/local/mailman/var/logs/cron.log 2>&1
Set up Mailman Web
UI
Postorius
andHyperkitty
areMailman
’s official Web UI and Archiver.Mailman-web
provides a convenient single package to install both of these. You can runMailman
3 without it, but most people prefer to use the web interface for changing list settings, viewing information about available lists, and subscribing or unsubscribing.Django
is thepython
web framework used to buildPostorius
andHyperkitty
.
Let's enter the virtual environment again and install Mailman web
:
su - mailman source bin/activate (mailman)$ pip install mysqlclient mailman-web mailman-hyperkitty pylibmc
We'll use the MySQL
database in a new mailman_web database (the owner will be mailman, who already owns the mailman database that we created earlier):
CREATE DATABASE `mailman_web` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
GRANT ALL PRIVILEGES ON `mailman_web`.* TO 'mailman'@'localhost';
The CHARACTER SET utf8mb4
parameter is important.
The ~/settings.py file will hold the settings for Mailman web
. Download and adjust it to your needs:
(mailman)$ wget -O ~/etc/settings.py https://notes.sagredo.eu/files/qmail/mailman/settings.py (mailman)$ chmod o-r ~/etc/settings.py
It's important to add your domain to the variables ALLOWED_HOSTS
and CSRF_TRUSTED_ORIGINS
. Here lists.mydomain.tld is the web server's virtual host used for the
Mailman
web site; I'm supposing that it could be the same as the domain where we decided to define the lists, but it can be a different one.
#: See https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts ALLOWED_HOSTS = [ "localhost", # Archiving API from Mailman, keep it. "127.0.0.1", "0.0.0.0", "lists.mydomain.tld", # Add here all production domains you have. ] #: See https://docs.djangoproject.com/en/dev/ref/settings/#csrf-trusted-origins #: For Django <4.0 these are of the form 'lists.example.com' or #: '.example.com' to include subdomains and for Django >=4.0 they include #: the scheme as in 'https://lists.example.com' or 'https://*.example.com'. CSRF_TRUSTED_ORIGINS = [ "http://lists.mydomain.tld", "https://lists.mydomain.tld", # "lists.your-domain.org", # Add here all production domains you have. ]
Add its location to the mailman's user .profile (default would be /etc/settings.py otherwise):
(mailman)$ cat >> ~/.profile << __EOF__ export DJANGO_SETTINGS_MODULE=~/etc/settings __EOF__
Be carefull when setting SITE_ID
. It is the ID
of the Django
Site being served, i.e. the site that will serve your Mailman web
control panel.
#: Current Django Site being served. This is used to customize the web host #: being used to serve the current website. For more details about Django #: site, see: https://docs.djangoproject.com/en/dev/ref/contrib/sites/ SITE_ID = 2
When you open the Mailman web
interface, you'll have an example.com site already created with SITE_ID = 1
that you may want to delete and replace with mydomain.tld, which will have SITE_ID =
2
or whatelse. Enter its value in settings.py file, presumibly it will be 2
.
Create the directory where the web interface will live:
mkdir -p ~/web/logs
In the following commands we have to pass some variables to mailman-web
. I don't know why it doesn't read those variables from env. Setup the database schema for Mailman
’s web components:
(mailman)$ PYTHONPATH=~/etc/ DJANGO_SETTINGS_MODULE=settings mailman-web migrate
Copy all the static files (css, js, images) into the STATIC_ROOT
:
(mailman)$ PYTHONPATH=~/etc/ DJANGO_SETTINGS_MODULE=settings mailman-web collectstatic
Compress the various CSS files offline (the sass
package is needed):
(mailman)$ PYTHONPATH=~/etc/ DJANGO_SETTINGS_MODULE=settings mailman-web compress
Update the message catalogs for supported languages:
(mailman)$ PYTHONPATH=~/etc/ DJANGO_SETTINGS_MODULE=settings mailman-web compilemessages
Now set up the admin account. The superuser is assumed to be the Site Owner and has access to all the domains, mailing lists and their settings. You'll be asked to enter and confirm the email address of the admin account. Do not use an account such as postmaster@lists.mydomain.tld because it would be difficult to retrieve the confirmation message. Use something like postmaster@mydomain.tld instead.
(mailman)$ PYTHONPATH=~/etc/ DJANGO_SETTINGS_MODULE=settings mailman-web createsuperuser
memcached
setup
Installing memcached
and the pylibmc
package that we have installed earlier is recommended to fix an issue concerning the erase of items in the archive (more info here), which can be solved installing a caching system that is common across all workers. memcached
installation can be easily done with a package of the OS and is not covered here; just be sure that it is launched at boot time and that it is listening on the 11211 port. memcached
settings are already stored in the settings.py file that we downloaded earlier:
# Using the cache infrastructure can significantly improve performance on a # production setup. This is an example with a local Memcached server. # pip install pylibmc CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': '127.0.0.1:11211', } }
Setting up a WSGI
server
WSGI
gets Django
to work behind apache
or another web server. Django
is the python
web framework used to build Postorius
and Hyperkitty
.
(mailman)$ pip install uwsgi
Download the configuration file (there's no need to modify it):
wget -O ~/etc/uwsgi.ini https://notes.sagredo.eu/files/qmail/mailman/uwsgi.ini
Test (exit with ^C
):
(mailman)$ uwsgi --ini ~/etc/uwsgi.ini
Startup script
Those under systemd
should refer to the official documentation here. Users who can do what they want with their system can use this scriptlet:
wget -O /usr/local/bin/uwsgictl https://notes.sagredo.eu/files/qmail/mailman/uwsgi.ini chmod +x /usr/local/bin/uwsgictl
Add it to your rc.local or init.d. Test as root:
# uwsgictl Usage: /usr/local/bin/uwsgictl {start|stop|restart|reload|force-reload} # uwsgictl start Starting uWSGI Server[uWSGI] getting INI configuration from /usr/local/mailman/etc/uwsgi.ini
The log files for both UWSGI
and Mailman Web
are under ~/web/logs.
Setting up Fulltext search (Xapian
)
To install both xapian-core
and xapian-bindings
in the virtual environment check the latest version number in the source site and pass it as an argument to the install script:
(mailman)$ wget https://raw.githubusercontent.com/notanumber/xapian-haystack/master/install_xapian.sh (mailman)$ sh install_xapian.sh 1.4.25
Install Xapian-Haystack
:
pip install xapian-haystack
Rebuild the index (force the rebuild):
(mailman)$ PYTHONPATH=~/etc/ DJANGO_SETTINGS_MODULE=settings mailman-web rebuild_index WARNING: This will irreparably remove EVERYTHING from your search index in connection 'default'. Your choices after this are to restore from backups or rebuild via the `rebuild_index` command. Are you sure you wish to continue? [y/N] Y Removing all documents from your index because you said so. All documents removed. Indexing 11 emails
The following are already set in your ~/etc/settings.py file that you downloaded earlier:
# xapian (full text search) HAYSTACK_CONNECTIONS = { 'default': { 'HAYSTACK_XAPIAN_LANGUAGE': 'en', 'ENGINE': 'xapian_backend.XapianEngine', 'PATH': "/usr/local/mailman/web/xapian_index" }, }
You can change the english default language modyfing the HAYSTACK_XAPIAN_LANGUAGE
variable. The list of available languages can be found here.
Then restart the server:
uwsgictl restart
Cronjob setup for Mailman Web
As the mailman user edit the crontab
(crontab -e
) and install the following cronjobs:
@hourly ID=mailman PYTHONPATH=/usr/local/mailman/etc/ DJANGO_SETTINGS_MODULE=settings /usr/local/mailman/bin/mailman-web runjobs hourly >/dev/null 2>&1 @daily ID=mailman PYTHONPATH=/usr/local/mailman/etc/ DJANGO_SETTINGS_MODULE=settings /usr/local/mailman/bin/mailman-web runjobs daily >/dev/null 2>&1 @weekly ID=mailman PYTHONPATH=/usr/local/mailman/etc/ DJANGO_SETTINGS_MODULE=settings /usr/local/mailman/bin/mailman-web runjobs weekly >/dev/null 2>&1 @monthly ID=mailman PYTHONPATH=/usr/local/mailman/etc/ DJANGO_SETTINGS_MODULE=settings /usr/local/mailman/bin/mailman-web runjobs monthly >/dev/null 2>&1 @yearly ID=mailman PYTHONPATH=/usr/local/mailman/etc/ DJANGO_SETTINGS_MODULE=settings /usr/local/mailman/bin/mailman-web runjobs yearly >/dev/null 2>&1 * * * * * ID=mailman PYTHONPATH=/usr/local/mailman/etc/ DJANGO_SETTINGS_MODULE=settings /usr/local/mailman/bin/mailman-web runjobs minutely >/dev/null 2>&1 2,17,32,47 * * * * ID=mailman PYTHONPATH=/usr/local/mailman/etc/ DJANGO_SETTINGS_MODULE=settings /usr/local/mailman/bin/mailman-web runjobs quarter_hourly >/dev/null 2>&1
Test:
(mailman)$ PYTHONPATH=~/etc/ DJANGO_SETTINGS_MODULE=settings mailman-web runjobs -l Job List: 11 jobs appname - jobname - when - help -------------------------------------------------------------------------------- django_extensions - cache_cleanup - daily - Cache (db) cleanup Job django_extensions - daily_cleanup - daily - Django Daily Cleanup Job hyperkitty - empty_threads - monthly - Remove empty threads hyperkitty - new_lists_from_mailman - hourly - Import new lists from Mailman hyperkitty - orphan_emails - daily - Reattach orphan emails hyperkitty - recent_threads_cache - daily - Refresh the recent threads cache hyperkitty - sync_mailman - daily - Sync user and list properties with Mailman hyperkitty - thread_order_depth - yearly - Compute thread order and depth for all threads hyperkitty - thread_starting_email - hourly - Find the starting email when it is missing hyperkitty - update_and_clean_index - monthly - Update the full-text index and clean old entries hyperkitty - update_index - hourly - Update the full-text index
The HyperKitty
archiver
HyperKitty
is an application providing a web interface to access Mailman
archives, and interact with the lists. To connect HyperKitty
with Mailman
, these are the lines already set in your mailman.cfg that you downloaded earlier:
# For the HyperKitty archiver. [archiver.hyperkitty] class: mailman_hyperkitty.Archiver enable: yes configuration: /usr/local/mailman/etc/mailman-hyperkitty.cfg
Now create mailman-hyperkitty.cfg (downolad)
(mailman)$ cat > ~/etc/mailman-hyperkitty.cfg << __EOF__ # This is the mailman extension configuration file to enable HyperKitty as an # archiver. Remember to add the following lines in the mailman.cfg file: # # [archiver.hyperkitty] # class: mailman_hyperkitty.Archiver # enable: yes # configuration: /path/to/here/mailman-hyperkitty.cfg # [general] # This is your HyperKitty installation, preferably on the localhost. This # address will be used by Mailman to forward incoming emails to HyperKitty # for archiving. It does not need to be publicly available, in fact it's # better if it is not. # However, if your Mailman installation is accessed via HTTPS, the URL needs # to match your SSL certificate (e.g. https://lists.example.com/hyperkitty). base_url: http://lists.mydomain.tld/archives/ # The shared api_key, must be identical except for quoting to the value of # MAILMAN_ARCHIVER_KEY in HyperKitty's settings. api_key: xxxxxxxxxxxxxxxxxxx __EOF__
It is important that the api_key
value matches the MAILMAN_ARCHIVER_KEY
value inside settings.py.
Apache
configuration
Here is an example of apache
virtual host. mod_proxy
must be enabled:
Define MMDIR /usr/local/mailman Define MMDOMAIN lists.mydomain.tld <VirtualHost *:443> ServerName ${MMDOMAIN} # Include SSL stuff CustomLog ${LOGDIR}/${MMDOMAIN}.log combined ErrorLog ${LOGDIR}/${MMDOMAIN}_error.log Alias /static "${MMDIR}/web/static" Alias /favicon.ico $MMDIR/web/static/hyperkitty/img/favicon.ico <Directory "${MMDIR}/web/static"> Require all granted </Directory> <IfModule mod_rewrite.c> RewriteEngine On # redirects / calls to /mailman3 RewriteRule ^/$ https://${MMDOMAIN}/mailman3 [R=302,L] </IfModule> <IfModule mod_proxy.c> SSLProxyEngine On ProxyRequests Off ProxyPreserveHost On ProxyPass "/mailman3" "http://127.0.0.1:8000/mailman3" ProxyPass "/archives" "http://127.0.0.1:8000/archives" ProxyPass "/accounts" "http://127.0.0.1:8000/accounts" ProxyPass "/admin" "http://127.0.0.1:8000/admin" ProxyPass "/user-profile" "http://127.0.0.1:8000/user-profile" ProxyPassMatch ^/static/ ! </IfModule> </VirtualHost>
Once finished browse to https://lists.mydomain.org/mailman3 and login with the superuser credentials. Then confirm the superuser's email address and login again.
logrotate
Here is a logrotate
file to rotate both Mailman
and Mailman web
files:
cat > /etc/logrotate.d/mailman << __EOF__ #mailman /usr/local/mailman/var/logs/*.log { missingok sharedscripts su mailman mailman postrotate # /usr/local/bin/mmctl reopen &>/dev/null || true # users with no systemd cd /usr/local/mailman sudo -u mailman /usr/local/mailman/bin/mailman -C /usr/local/mailman/etc/mailman.cfg reopen &>/dev/null || true endscript } # mailman-web /usr/local/mailman/web/logs/*.log { hourly missingok notifempty delaycompress su mailman mailman } __EOF__
Adding domains
With the previous instructions all the lists belong to the same domain (lists.mydomain.tld in our example). What do you need to do if you want to create lists on other domains, say otherdomain.tld?
Create a Linux
user and link the virtualdomain otherdomain.tld to that user:
useradd -d /home/username username mkdir -p /home/username chown -R username:users /home/username echo otherdomain.tld:username >> /var/qmail/control/virtualdomains
In this way qmail
will seek the dot-qmail
file which is responsible to handle the delivery in the user's home directory. Therefore we have to save there the instructions to call the Mailman
's LMTP
server:
echo "|/usr/local/mailman/qmail-lmtp 8024 1" > /home/username/.qmail-default chown username:users /home/username/.qmail-default
You may think that the same result could be achieved simply creating an alias /var/qmail/alias/.qmail-username-default but this method woudn't work, because qmail
would pass Mailman
the recipient address username-listname@otherdomain.tld instead of listname@otherdomain.tld and you'll get a "mailbox not found" reject.
As you know Django
comes with a “sites” framework. It’s a hook for associating objects and functionality to particular websites, and it’s a holding place for the domain names and “verbose” names of your Django
-powered sites.You should have already created the lists.mydomain.tld site with SITE_ID=2
or whatever as SITE_ID
. When you create your lists belonging to the newly created domain in the Postorius
web panel, you'll have to choose lists.mydomain.tld as its "Web server".
Adding mailing lists to your main domain
You may be wondering if you can define a list like list@mydomain.tld, where mydomain.tld is a virtual domain with ordinary qmail
/vpopmail
users and with its own .qmail-default file. The answer is yes. Just create the dot-qmail
file with the name that you want to assign to the list, call Mailman
in the usual way and you're done:
echo "|/usr/local/mailman/qmail-lmtp 8024 1" > ~vpopmail/domains/mydomain.tld/.qmail-list
Known issues
Mailman web
crashes with "AttributeError: 'NoneType' object has no attribute 'thread'" as soon as I click on the "Reattach this conversation" button in theHyperKitty
archive. I opened a ticket here.- As already mentioned, the original
qmail-lmpt
script doesn't work withMailman3
becauseqmail-local
sends bare LF for the end line, while theSMTP
standards require CRLF, so theSMTP
conversation betweenqmail
andMailman
ends up with the error: "Line too long (see RFC5321 4.5.3.1.6)". The patchedqmail-lmtp
file that we are using here can have unpredictable results when binary files with \n and \r characters are sent, according to what a developer says in this conversation. Anyhow it works fine here in my server so far.
List owner instructions
If your users ask you to provide a manual for the list administrator you can suggest this video guide, which covers everything.
The manual for the list member is in the official guide here.