Mailman 3

I'm trying to log my progress for anyone interested in installing Mailman 3..

pkg install python3

pkg install py36-sqlite3

python3 -m ensurepip

pip3 install mailman

mailman info - this creates the mailman subdirectory tree in the current directory.

Trying to figure out the directory structure that Mailman 3 on FreeBSD should adopt. Here is what is used on Linux:-

I'd be interested in the FreeBSD Gurus view on how this should be configured on FreeBSD:-

bin_dir: /sbin
var_dir: /var/lib/mailman
queue_dir: /var/spool/mailman
log_dir: /var/log/mailman
lock_dir: /var/lock/mailman
etc_dir: /etc
ext_dir: /etc/mailman.d
pid_file: /var/run/mailman/

I have followed your notes and installed MM3 on my FreeBSD 11.2.
I have opted to use /opt/mailman3 as the directory for MM3.
So I did:

cd /opt
mkdir mailman3
cd mailman3
mailman info

The directory has been populated with some data.

# Then from
pip3 install postorius
pip3 install hyperkitty
pip3 install mailman-hyperkitty
pip3 install uwsgi

All those installed successfully. However, I am not sure where they installed into :)

What next now?? Edit the var/etc/mailman.cfg??

Then how to launch the queue runner? Postorius? HyperKitty? etc?

I tried mailman3 today. The story now is even shorter, when installing on 12.1-RELEASE-p10 after installing the pkg. It advises to execute service mailman oneinfo to set configuration etc. That already fails:

The port itself shows this:

To initialise Mailman's runtime directories, log files and database for
the first time, run service mailman info You can then start Mailman by
running: service mailman start

After doing so , I get this error:

root@vm1:/zroot/zdata/howdoyoudo/freebsd # service mailman oneinfo
Traceback (most recent call last):
"[I]/usr/local/lib/python3.7/site-packages/pkg_resources/[/I]", line
583, in _build_master
"[I]/usr/local/lib/python3.7/site-packages/pkg_resources/[/I]", line
900, in require
    needed = self.resolve(parse_requirements(requirements))
"[I]/usr/local/lib/python3.7/site-packages/pkg_resources/[/I]", line
791, in resolve
    raise VersionConflict(dist, req).with_context(dependent_req)
pkg_resources.ContextualVersionConflict: (zope.interface 4.6.0
Requirement.parse('zope.interface>=5.0'), {'mailman'})

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/bin/mailman", line 6, in <module>
    from pkg_resources import load_entry_point
"[I]/usr/local/lib/python3.7/site-packages/pkg_resources/[/I]", line
3251, in <module>
"[I]/usr/local/lib/python3.7/site-packages/pkg_resources/[/I]", line
3235, in _call_aside
    f(*args, **kwargs)
"[I]/usr/local/lib/python3.7/site-packages/pkg_resources/[/I]", line
3264, in _initialize_master_working_set
    working_set = WorkingSet._build_master()
"[I]/usr/local/lib/python3.7/site-packages/pkg_resources/[/I]", line
585, in _build_master
    return cls._build_from_requirements(__requires__)
"[I]/usr/local/lib/python3.7/site-packages/pkg_resources/[/I]", line
598, in _build_from_requirements
    dists = ws.resolve(reqs, Environment())
"[I]/usr/local/lib/python3.7/site-packages/pkg_resources/[/I]", line
786, in resolve
    raise DistributionNotFound(req, requirers)
pkg_resources.DistributionNotFound: The 'zope.interface>=5.0'
distribution was not found and is required by mailman

It's a fresh install and it fails on the first command, I wonder what I
should do next...
PR 225543 put Mailman 3 into the ports tree.

I have recently updated PR 250362 with a list of how to get Mailman running, but not necessarily functional.

My next step, porting Postorius to FreeBSD. If anyone is available to help, please let me know.
Hi everybody :
I've just finished the migration from mailman2 to a mailman3 for two servers and a bunch of lists (17 in one server 2 in the other). The servers are running 12.2-RELEASE (with python 3.7 -and 2.7- installed from ports).
I begun trying to install it from ports, following Mr. Langille recommendations but I faced a hell of dependencies issues. Then I followed the installation guide in, but out of docker or virtualenv environment. I made a mix of ports and pip installs as long I need.
The servers are jailed and both running apache24 but are proxied from a nginx server each one acting as firewall/proxy (that hosted the jails). For the wsgi process I installed mod-wsgi and uwsgi for hyperkitty
All is working like a charm now, but It was really difficult to find the correct config for all the processes involved.
If there is interest in this aproximation I will polish my notes and put them here.
Yes, please especially the missing dependencies and the configurations.
Well.. first of all I need to express my thankfulness to all of you who work in making FreeBSD the better and solid platform, and in this moment, with python 2.7 in its EOL, the migration of mailman2 to mailman3 was a big thing. Thanks a lot dvl@.
(and beg all of you your pardon if my english is misspelled or confusing: I think in spanish)

The start point:
2 servers upgraded to 12.2-RELEASE acting as firewalls/proxies in separated lines (one ADSL, the other optic fiber), with static IPs. Each of them had running sendmail + mailman2 + nginx and are the hosts of a number of jailed servers who serve Drupal or Wordpress sites (with apache24). The two had identical configurations. They have letsencrypt certificates (but some of them are proxied by cloudflare, too)
For each jailed server there is a mount point to mailman2 (/usr/local/mailman), the ports(/usr/ports) and the certificate from the host server(/usr/local/etc/letsencrypt).
All the mail treatment occurs in the main host with sendmail (by the way: I changed to sendmail from ports, as some time ago, two or three years ago if I recall it well, it was easier to config for auth mail and other things), but you can access to the web config in the jailed servers as they have mailman2 installed an accessible via the mount point. In the jailed servers the MTA was sendmail, but only with submit activated.
Lets say I have (ADSL line) as the main host(local IP, and as jailed host (local IP with 2 mailman lists. For the optic fiber line I have (local IP and as its jailed host (local IP with 17 mailman lists.

I hope it pictures well the panorama from I started...

As I said in my first comment, one of the jailed servers had just 2 lists in mailman, and it was the "target" to try and test the migration: they have not much email traffic. It served a Drupal site with apache24 (which means that I have to combine the new mailman3 Django project underlying).

[[[ I'm going to focus in this post in the tryings and "what didn't work well" things and in a separate post I will list the steps in the other server which work well at the end without all the frustration]]]

- The first thing I thought I had to do was to change the MTA in the jailed server ( - mostly all the help guides are prepared to use postfix as MTA, so I did a "portsnap fetch update" and installed postfix form ports. I followed . It needed some adjusts for mailman3 but I applied them after (we'll come back to the postfix and sendmail in the main server config in the "all went well" post)

(WARNING: all the python installs from ports where with FLAVOR=py37 as I have a running python 2.7 apps yet)

- I followed the post of dvl@ in this thread, made a make install of /usr/ports/mail/mailman3 and
it went well. As he mentioned, I was expecting dependencies problems ( For the steps in the whole installation I tried to follow, but out the virtualenv enviroment (I've noticed some folks having serious problems with virtualenv in freebsd, and I have no experience with it).
After I had mailman3 running I tried to install and config the Django ports. I found that the perl version was too low (5.26), and then upgraded all perl dependencias to 5.30. And here I fell into hell: it needed upgrade rust (which I upgrade from pkg, because from ports it will take DAYS to compile). It had conflicts with ImageMagick and php-gd... and many many more in a loop.

This was the moment I decided to uninstall Django from ports and try to "pip install" what I need. At the end I reinstalled all the mailman apps from pip and configured all thereafter.
Next post I show the steps that went well from the beginning.
Last edited:
As I said in my previous post (please review the first part of it to see all the picture), after I made a succesfull mailman3 installation in one server with 2 mailman2 lists, I did it again with a jailed server with 17 mailman2 lists.
The servers: (local IP and as its jailed host (local IP with 17 mailman lists.
The scope:
postfix config files in /usr/local/etc/postix (I attach as postfix-main.conf with all comment lines stripped)
postfix virtual users in /usr/local/etc/postfix/virtusertable
mailman3 services in /usr/local/mailman3
/usr/local/bin/python3.7 has a ln -s to /usr/local/bin/python3 (I had python linked to the 2.7 version yet)

In the jailed server (
- portsnap fetch update

- install /usr/ports/mail/postfix

- followed the changing MTA from sendmail (only submit) to postfix guide

- created user for sasl2 authentication in /usr/local/etc/postfix/sasl_passwd and postmap it
[]:587 mailoutuser: passoutuser
(there is no space after the colon, but this wysiwyg translate as a :p, so I put one here)
(you have to create the user in the main server with "saslpasswd2 -c" )

- installed /usr/ports/devel/py-pip with FLAVOR=py37

- did a "ln -s /usr/local/bin/py37-pip to /usr/local/bin/pip" in order to clarity and follow guides

- pip install mailman

- installed /usr/ports/textproc/sassc and did an "ln -s /usr/local/bin/sassc /usr/bin/sassc" for django to find it (important)

- pip install mailmanclient

- created user mailman3 with the same gid and uid as mailman (1113:1113 in my system) and with /usr/local/mailman3 as home .

- mkdir /usr/local/mailman3 & chown mailman3:mailman /usr/local/mailman3

- install /usr/ports/devel/git

- cd /usr/local

- git clone
That clones mailman-suite and bring us some files to get config

- cd mailman-suite/mailman-suite_project
- su -m mailman3 -c " cp, and /usr/local/mailman3"
(you can now delete /usr/local/mailman-suite as is no longer needed)

I attach the mailman.conf file that would be /usr/local/mailman3/var/etc/mailman.cfg

[[[Post edition 12/27/20:
I've found that I missed to define one important thing in mailman.cfg
The site owner as it is the "from" address which sends you the unsuscribe notifications, etc.
You can see all the config possibilities in
I've added these lines in the top of my mailman.cfg file
default_language: es
(the second line, obviously, is not necessary for your config)

- installed /usr/ports/databases/py-pymysql as I want that mailman uses mysql database (and let Django with Sqlite3)

- Created a "mailman" database in mysql and granted permissions for IP, localhost and host "" accessing with "mysql -p -u root" and executing (I just put here the last grant)
"GRANT all privileges on mailman.* to 'mailman3'@'' IDENTIFIED BY 'databasepass' WITH GRANT OPTION;"

- installed
pip install postorius
pip install hyperkitty
pip install mailman-hyperkitty
pip install whoosh
"python3 -m django --version" shows 3.0.11 (that's it)

- installed /usr/ports/databases/py-sqlite3

I attached the needed that would be /usr/local/mailman3/ for Django
(Obtained and modified from the previous git run)

su -m mailman3 -c "mkdir /usr/local/mailman3/logs"
su -m mailman3 -c "touch /usr/local/mailman3/logs/mailmansuite.log"

Last thing:
As I installed postfix in /usr/local, mailman cannot find the postmap command to hash de virtual tables, then I did (using bash)
for fic in `ls /usr/local/sbin/post*`; do ln -s $fic /usr/sbin/`basename $fic`; done

- cd /usr/local/mailman3

- su -m mailman3 -c "python3 migrate"
(This creates all the things needed)

- su -m mailman3 -c "python3 collectstatic"

- su -m mailman3 -c "python3 createsuperuser"
(the admin: in my example is

**** at this point you have all the django settings made. Before doing the apache24 part I have to say a couple of things:
In the virtualenv install guide stated you can start an instance of server with a
"python3 runserver". That's good to test the settings... but can confuse you because it's not needed at all if you have a "real server" like apache or nginx serving http. At least it have me missleaded for many days.
The hyperkitty config base path: it took me much frustrating time of confusion as in the guides it stated just as "http://localhost/hyperkitty"... and it not archives any mail at all untill I understand that it have to be as it shows in my config file (well, I changed the access for "mailman3" but it would be preferable if it was noted as, say, "http://localhost/hyperkitty/hiperkitty"
For the apache24 part I decided to use the mod-wsgi (and uswgi binary).
- pip install mod-wsgi
(with "mod_wsgi-express module-config" it shows the conf needed.)

- pip install uwsgi

- Included these lines in the "loadmodule" part of /usr/local/etc/apache24/httpd.conf
LoadModule wsgi_module "/usr/local/lib/python3.7/site-packages/mod_wsgi/server/"
WSGIPythonHome "/usr/local"
WSGIDaemonProcess hyperkitty threads=25 python-path=/usr/local/mailman3 user=mailman3 group=mailman
WSGIProcessGroup hyperkitty
- Included this line in the correct places of my /usr/local/etc/apache24/extra/httpd-vhosts.conf (in the listen 80 and 443 servers)
Include "/usr/local/mailman3/apache24.uwsgi.conf"
(I attach my apache24.uwsgi.conf. Remember I have a Drupal site enabled here)

- service apache24 restart

- initiated the uwsgi with a
/usr/local/bin/uwsgi --ini /usr/local/mailman3/uwsgi.ini &
(beware the "&" backgrounding it)
As I can only attach 5 files I put it here
####### start of /usr/local/mailman3/uwsgi.ini
# uwsgi.ini
# Port on which uwsgi will be listening.
http-socket =
# Move to the directory wher the django files are.
chdir = /usr/local/mailman3/
# Use the wsgi file provided with the django project.
wsgi-file =
# Setup default number of processes and threads per process.
master = true
process = 2
threads = 2
# Drop privielges and don't run as root.
uid = www
gid = www
# Setup the django_q related worker processes.
attach-daemon = ./ qcluster
# Setup the request log.
req-logger = file://usr/local/mailman3/logs/uwsgi.log
# Log cron seperately.
logger = cron file://usr/local/mailman3/logs/uwsgi-cron.log
log-route = cron uwsgi-cron
# Log qcluster commands seperately.
logger = qcluster file://usr/local/mailman3/logs/uwsgi-qcluster.log
log-route = qcluster uwsgi-daemons
# Last log and it logs the rest of the stuff.
logger = file://usr/local/mailman3/logs/uwsgi-error.log
####### end of /usr/local/mailman3/uwsgi.ini

And thats all about mailman 3 configuration. You can start all with two commands
/usr/bin/su -m mailman3 -c "/usr/local/bin/mailman -C /usr/local/mailman3/var/etc/mailman.cfg start"
/usr/local/bin/uwsgi --ini /usr/local/mailman3/uwsgi.ini &
I created a to start, stop and restart all when needed if someone wants it I'll attach it in other post.

Then you have to create the lists in postorious and migrate following the guide in
It works very very well (I migrated all the lists without any problem)

One thing I did is declare as domain "" and as alias "" in the postorious config for adding domains.

You do remember that was jailed, isn't it? And the MX server in DNSs for is That implies that we need to "resend" the mail for the lists from to (as with mailman2 was mercurio the server that manages mailman2 lists).

To do that I use a virtual user table in postfix I named "virtusertable" (like in sendmail). That's why I append in muy destinations in postfix.

Say you have the list one_list@

I created in /usr/local/etc/postfix/virtusertable the following lines:

# Virtual mappings for the domain.
I did not copied here all the lineas needed, but you can get them easily doing a
"cat /usr/local/mailman3/var/data/postfix_vmap >> /usr/local/etc/postfix/virtusertable"
and adding the "" entries for each list.

And don't forget to postmap /usr/local/etc/postfix/virtusertable (and service postfix reload after that)

That sends all email for lists to the correct addresses

To manage that in I configured the local-unbound process, and, for sendmail, configured /etc/mail/mailertable and /etc/mail/virtusertable.

in mercurio:
# local zone
private-domain: ""
local-zone: "" static
local-data: " IN A"
local-data: " IN A"
local-data: " IN MX 10"
local-data-ptr: ""

/etc/mail/mailertable: smtp:[]

... (rest of the hooks for the list)

The reason to do it this way (using a "fake" server) is because I didn't achieve to resend emails in other way as mailertable did not work.

And that's all...


  • potsfix-main.conf
    2.3 KB · Views: 203
  • mailman.conf
    1.9 KB · Views: 231
  • mailman-hyperkitty.conf
    173 bytes · Views: 218
    8.3 KB · Views: 226
  • apache24.uwsgi.conf
    1.4 KB · Views: 229
Last edited:
A few days ago I did an upgrade of the mailman3 related apps (mailman from 3.3.2 to 3.3..3, e.g.). I write down here the process to fulfill a successfull upgrade (for anyone who access in the future this posts and have the same environment):
I did, as superuser:
pip install --upgrade django-mailman3 mailman mailman-hyperkitty mailmanclient postorius django hyperkitty

Then (be aware that mailman3 here is the user that runs mailman), in the mailman3 main directory did:
su -m mailman3 -c "python3 collectstatic"
su -m mailman3 -c "python3 migrate"

There are recommendations that you execute also a
su -m mailman3 -c "python3 compress"
su -m mailman3 -c "python3 compilemessages"

But I didn't and all my mailman3 lists are working as before. I think it depends of your config.

[[[Post edition on 02/16/21: If you need to update your translation as e.g. using spanish you may want do the "compilemessages" run. But if you, as myself, ended installing all using pip, you will find an error if you try to do as I put it above. For the sake of python doing it well, you have to move to each python package directory and do it using django-admin command as superuser.
E.G. you have python3.7 installed. it had to be done, at least for postorius, hyperkitty and django_mailman3:
cd /usr/local/lib/python3.7/site-packages/postorius
django-admin compilemessages
and so on for the other two.]]]

I couldn't find any guide about this process of upgrading and I just did the "migrate" part the first try. It showed that you cannot access the lists via postorious falling in a extrange exception. After reinstallating all, I realized that the "collectstatic" part was necesary because it's not invoked in the "migrate" run.
Last edited:
Oldno7 where is the startup script? The one for /usr/local/etc/rc.d/. And where is the cron file?
I am about to embark on this.
I had hope the FreeBSD port would have encompassed all this by now.
I gave up on moving to Mailman 3, mostly because of the complexity. My work there has ended.

I will be moving to mlmmj, much like the FreeBSD mailing lists are moving there.
I gave up on moving to Mailman 3, mostly because of the complexity. My work there has ended.

I will be moving to mlmmj, much like the FreeBSD mailing lists are moving there.
I did not understand why the other needed to be so complicated and installed mlmmj from the beginning.

Why the hell needs mailman for example to run a daemon?!
Oldno7 where is the startup script? The one for /usr/local/etc/rc.d/. And where is the cron file?
I am about to embark on this.
I had hope the FreeBSD port would have encompassed all this by now.
First of all.. my apologies. I didn't read this till today... more than a year after.
For the startup script, I write it myself. I'll copy it down.
For the /etc/crontab, I did follow the docs:
*-*-*-*-*-*-*-*-*-* (snip of the /etc/crontab)
### mailman3
@hourly www django-admin runjobs hourly --pythonpath /usr/local/mailman3 --settings settings
@daily www django-admin runjobs daily --pythonpath /usr/local/mailman3 --settings settings
@weekly www django-admin runjobs weekly --pythonpath /usr/local/mailman3 --settings settings
@monthly www django-admin runjobs monthly --pythonpath /usr/local/mailman3 --settings settings
@yearly www django-admin runjobs yearly --pythonpath /usr/local/mailman3 --settings settings
* * * * * www django-admin runjobs minutely --pythonpath /usr/local/mailman3 --settings settings
2,17,32,47 * * * * www django-admin runjobs quarter_hourly --pythonpath /usr/local/mailman3 --settings
0 12 * * * mailman3 cd /usr/local/mailman3 && /usr/local/bin/mailman -C /usr/local/mailman3/var/etc/mailman.cfg digests --periodic
0 8 * * * mailman3 cd /usr/local/mailman3 && /usr/local/bin/mailman -C /usr/local/mailman3/var/etc/mailman.cfg notify
*-*-*-*-*-*-*-*-*-* (end of snip)

Hope this helps anybody.

The /usr/local/etc/rc.d/ file:
# PROVIDE: mailman3
# REQUIRE: apache24 postfix mysql-server
# KEYWORD: shutdown

export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
export HOME=/usr/local/mailman3

cd $HOME;

case "$1" in
/usr/bin/su -m mailman3 -c "$MAILMAN -C $MAILMANCFG start --force"
/usr/bin/su -m mailman3 -c "$MAILMAN -C $MAILMANCFG stop"
/bin/pkill -9 -a -f uwsgi

/usr/bin/su -m mailman3 -c "$MAILMAN -C $MAILMANCFG restart"

cd $WCD;
exit 0