I recently set up Mailman, a mailing list manager, on a small VPS running Debian GNU/Linux. I also set up Postfix as my MTA. It took me a week of evenings and I had a hard time getting it done. I collected my notes along the way so that they may benefit someone else.

I’ve organized my notes as a commentary on the official installation documentation. The official documentation is comprehensive, but it makes a number of assumptions that don’t apply to this particular installation scenario. I hope my commentary can act as a helping hand for those in a similar situation (i.e. installing Mailman 1.2.1 and Postfix 2.11.3 using apt on Debian 8 (Jessie)). As you read the Postfix Basic Configuration and the GNU Mailman Installation Manual, you can refer to my notes on each section, below. This is not a complete guide and won’t make much sense without the installation manual.

Note that I already had Python 2.7.9, Apache 2.4.10, and msmtp 1.4.32 installed and configured. I have also registered my domain, example.com, and set up a DNS A record that points example.com to my VPS. I want to host my lists at lists.example.com which will require me to use a virtual domain. I’m using example.com as a placeholder. You will need to use your chosen domain during your installation.

Basic Installation

To install Mailman and Postfix using apt, I typed

  sudo apt install mailman postfix

at the command line. The Postfix installer asked me to provide a FQDN. I gave it example.com. Mailman asked me to select a language. I selected English.

Postfix Configuration

I followed the Postfix Basic Configuration guide. I reviewed all the defaults and almost all of them looked correct for my situation. In /etc/postfix/main.cf, I added

  mydomain = example.com

Looking at the chroot discussion, the readme suggests that “Postfix daemons that deliver mail locally” can’t be run chrooted. I will certainly be delivering mail locally.

I also created an MX record for example.com and opened port 25 to TCP connections. At this point, you should be able to restart Postfix

sudo /etc/init.d/postfix restart

and send a test email to a local user (e.g. dbucklin@example.com).

If your test fails, refer to the Troubleshooting section at the end of this document.

Mailman Configuration

1 Installation Requirements

We are not going to be installing from source, but do verify that you have a recent patch of python 2.7 installed. As I write this, the Mailman wiki recommends Python 2.7.14. I had Python 2.7.9 installed.

This section also says, “you will need an ANSI C compiler to build Mailman’s security wrappers.” I assume this only applies if you are installing from source. I didn’t compile anything directly during this process.

2 Set up your system

The manual suggests creating a user and group called mailman. I installed Mailman using apt. This install process created the list user and group, so I didn’t need to create any users or groups, and I didn’t have to create an installation directory or manipulate permissions at this point.

3 Build Mailman

I didn’t install from source, so there’s no need to do any of this.

4 Check your installation

I ran check_perms with the -f switch, but I still had to change some files manually so that they were owned by root:list. For my installation, the value of $prefix was /usr/lib/mailman.

5 Set up your web server

The Debian distribution of Mailman comes with a sample apache.conf file that is actually fairly complete for this purpose. I started by appending those settings to my apache2.conf file.

cat /etc/mailman/apache.conf >> /etc/apache2/apache2.conf

This covered the majority of the individual steps in this section. I had to change of

Order allow,deny
Allow from all


Require all granted

It’s not addressed in the installation manual, but I had to enable the Apache CGI module.

sudo a2enmod cgid

Before I did this, apache was serving up binary files instead of running scripts.

I continued to have problems until I restarted my browser.

In /etc/mailman/mm_cfg.py I added

DEFAULT_EMAIL_HOST = 'lists.example.com'
DEFAULT_URL_HOST = 'lists.example.com'
DEFAULT_URL_PATTERN = 'http://%s/cgi-bin/mailman/'

and commented out the existing settings for those values.

I restarted apache with

sudo apachectl restart

but you could also use

sudo /etc/init.d/apache restart

Apache says,

AH00112: Warning: DocumentRoot [/var/www/lists] does not exist

which makes sense since there’s no such folder.

I can now access my Mailman web interface at http://example.com/cgi-bin/mailman/admin

I found that the create list function was publicly available. I didn’t want that, so I had to disable it. I didn’t find a cleaner way to do this, I just changed the permissions on the create script.

sudo chmod o-x /usr/lib/cgi-bin/mailman/create

6 Set up your mail server

I’m using Postfix because I heard it was the easiest to set up.

6.1 Using the Postfix mail server

The only thing I did in this section was add

unknown_local_recipient_reject_code = 550

to /etc/postfix/main.cf.

6.1.1 Integrating Postfix and Mailman

I have a number of notes on this section.

Debian includes a script, postfix-to-mailman.py, that claims to make alias management easier. I did not have success with it. A number of sources I found indicated that using postfix-to-mailman.py is neither supported nor recommended. My advice is to ignore it and stick to the official install manual. The script is only distributed with Debian distributions of Linux.

I don’t understand why 6.1.1 and 6.1.2 are not reversed. If you are using virtual domains (e.g. lists.example.com), then you will need to configure them in Mailman before you can generate aliases. If you are using virtual domains, you’ll need to skip ahead to section 6.1.2 and then come back here.

There is a typo in section 6.1.1 in the note that recommends reading the next section first. The copy says, “read the 6.1 section below first,” but the link points to section 6.1.2.

After setting MTA = ‘Postfix’ in /usr/lib/mailman/Mailman/mm_cfg.py, and before running genaliases, you need to create a list in your virtual domain. Something like:

newlist -e lists.example.com testlist

Now, when you run genaliases,

sudo /usr/lib/mailman/bin/genaliases

it should create


Note that, though the install location for Mailman is /usr/lib/mailman, the map files created by genaliases live in /var/lib/mailman/data.

I changed the ownership and permissions on these files.

sudo chown list:list /var/lib/mailman/data/{aliases,aliases.db,virtual-mailman,virtual-mailman.db}
sudo chmod g+w /var/lib/mailman/data/{aliases,aliases.db,virtual-mailman,virtual-mailman.db}

Restart Postfix

sudo /etc/init.d/postfix restart

Restart Mailman

sudo /etc/init.d/mailman restart

6.1.2 Virtual domains

If you are using virtual domains, (e.g. lists.example.com), then DNS will need to be working for these domains. My domain configuration at the registrar refers to my VPS provider’s name servers. In my VPS’s DNS configuration, I added DNS records for my virtual domain.

added A record for lists.example.com
added mx record for lists.example.com

In /usr/lib/mailman/Mailman/mm_cfg.py add


Setting up virtual alias domains in Postfix involved:

Update alias maps in /etc/postfix/main.cf

alias_maps = hash:/etc/aliases,hash:/var/lib/mailman/data/aliases
alias_database = hash:/etc/aliases
virtual_alias_domains = lists.example.com
virtual_alias_maps = hash:/var/lib/mailman/data/virtual-mailman

I copied /etc/aliases to /var/lib/mailman/data/aliases, but I’m not sure it was necessary.

Now, return to 6.1.1 to create your list and generate aliases.

7 Review your site defaults

I skipped this part, assuming that the defaults would be fine.

8 Create a site-wide mailing list

In /usr/lib/mailman

sudo bin/newlist mailman


At this point, you should have a working Mailman server. If you are not getting mail, but think you should, here’s where to start.

  • You should be able to open a telnet connection to lists.example.com on port 25. If you can’t, check your DNS and firewall settings.
  • Check your spam folder!
  • You can inspect mail that is queued for delivery by using mailq or /usr/sbin/postqueue -p. They appear to do the same thing.
  • You can inspect mail system activity at /var/log/mail.log. If you can spare a window, you might run tail -f /var/log/mail.log to keep an eye on things.

Wait. Why?

It has been pointed out to me that maintaining a mailing list can be a lot of work. There are security issues to think about, spam to fight, upgrades to apply, and let’s just ignore the human element for now. I agree that to do this right can be a lot of work. From a practical perspective, I’m taking this one step at a time. At a higher level, this is an expression of my dissatisfaction with a toxic social media environment. I want to see a return to a state where these communication mediums are built and maintained for the benefit of all.


Here’s a pile of resources that I found during this adventure.

  1. Postfix Basic Configuration
  2. https://www.linux.com/learn/install-and-configure-postfix-mail-server
  3. GNU Mailman Installation Manual
  4. https://askubuntu.com/questions/422689/mailman-web-interface-not-working
  5. https://mail.python.org/pipermail/mailman-users/2007-April/056639.html
  6. https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=718284
  7. Early mail rejection with Mailman in Postfix - Server Fault
  8. Postfix and Mailman - Server Fault
  9. ubuntu - Postfix-Mailman “Recipient address rejected: User unknown in local recipient table” - Stack Overflow
  10. How do I configure postfixtomailman.py?
  11. http://www.postfix.org/qmgr.8.html
  12. http://www.postfix.org/trivial-rewrite.8.html
  13. http://www.postfix.org/transport.5.html
  14. http://www.postfix.org/virtual.5.html
  15. http://www.postfix.org/VIRTUAL_README.html
  16. https://www.suse.com/support/kb/doc/?id=3279773
  17. https://mail.python.org/pipermail/mailman-users/2007-April/056640.html
  18. https://mail.python.org/pipermail/mailman-users/2012-October/074154.html
  19. https://www.gnu.org/software/mailman/mailman-admin.pdf
  20. https://wiki.list.org/DOC/Making%20Sure%20Your%20Lists%20Are%20Private