2026年11月12日

2026年11月12日

Create a Mailing List with Postfix: aliases :include: and Mailman 3

This article is a localized edition of the Japanese original. Read the Japanese original

There are two main ways to create a mailing list with Postfix.

Method Best for What it does
:include: in /etc/aliases Forwarding to a small group, such as staff Forwards mail sent to one address to everyone listed in a file
Integration with Mailman 3 Membership-based mailing lists Subscribe and unsubscribe, holding posts from non-members, notifications to the list owner, archives

The steps in this article were tested through actual delivery with the Debian 13 packages of Postfix 3.10 and mailman3 3.3.10 (as of November 2026). Ubuntu 24.04 also has a package with the same name (mailman3 3.3.8).

Note that the older Mailman 2 (the newlist and add_members commands, mm_cfg.py) was written for Python 2 and is not included in current Debian and Ubuntu releases. Use Mailman 3 for new lists.

Prerequisites

  • Postfix is running and can send and receive mail with the outside world
  • The MX record of the domain you use for lists (for example lists.example.com) points to this server
  • You work with root privileges

If you have not finished the basic Postfix setup, do that first.

Method 1: Create a forwarding list with /etc/aliases

Write the list members

Create a file with one member address per line.

# /etc/postfix/lists/staff
alice@example.com
bob@example.net

Register the alias

Add the following two lines to /etc/aliases. owner-staff is the address that receives delivery error notices.

# Append to /etc/aliases
staff: :include:/etc/postfix/lists/staff
owner-staff: root
newaliases

Mail sent to staff@(this server's domain) is now forwarded to everyone in the file. The member file is read on every delivery, so adding or removing members only requires editing the file (newaliases is needed only when you change /etc/aliases).

Check it

printf 'Subject: staff test\n\ntest\n' | sendmail staff@example.com
grep 'orig_to=<staff@' /var/log/mail.log | tail

If the log shows orig_to=<staff@…> and status=sent for each member, forwarding works.

With this method, anyone (including outsiders) who sends to this address reaches every member. There is no way to accept subscriptions and unsubscriptions or to stop posts from non-members. If you need that, use Method 2.

Method 2: Integrate with Mailman 3

Mailman 3 receives incoming mail from Postfix over LMTP (port 8024 by default) and hands delivery to members back to Postfix.

Step 1: Install

apt update
apt install mailman3

If you also want the web admin interface (Postorius) and archives (HyperKitty), install the mailman3-full package. The web interface needs its own web server and database setup, so this article covers command-line operation.

Step 2: Check the Mailman settings

The configuration file is /etc/mailman3/mailman.cfg. In the Debian package, the [mta] settings for Postfix integration are already in place.

[mta]
incoming: mailman.mta.postfix.LMTP
outgoing: mailman.mta.deliver.deliver
smtp_host: localhost
smtp_port: 25
lmtp_host: 127.0.0.1
lmtp_port: 8024
configuration: python:mailman.config.postfix

site_owner in the [mailman] section is still set to changeme@example.com, so change it to the administrator's address.

[mailman]
site_owner: postmaster@example.com

Then restart Mailman.

systemctl restart mailman3
systemctl status mailman3

Step 3: Change the Postfix settings

Every time a list is created, Mailman writes lookup tables for Postfix (postfix_lmtp and postfix_domains, each with a .db file) to /var/lib/mailman3/data/. Make Postfix read these tables so that mail for list addresses is handed to Mailman.

# Add the list domain to the domains this server receives for (append to the existing value)
postconf mydestination
postconf -e 'mydestination = $myhostname, localhost, lists.example.com'

# Use + as the address delimiter (Mailman uses it for bounce handling)
postconf -e 'recipient_delimiter = +'

# Hand mail for list addresses to Mailman (LMTP)
postconf -e 'transport_maps = hash:/var/lib/mailman3/data/postfix_lmtp'

# Add list addresses to the accepted recipients (appended to the default value)
postconf -e 'local_recipient_maps = proxy:unix:passwd.byname $alias_maps hash:/var/lib/mailman3/data/postfix_lmtp'

The mydestination example adds lists.example.com to an existing value. Check the current value with postconf mydestination and add to it without removing anything. If you already use transport_maps or local_recipient_maps, append to the existing values as well (the official Mailman documentation also says to add to existing settings).

The table files are created when you create a list in Step 4, so reload Postfix after creating the list.

Step 4: Create a list and add members

On Debian, run the mailman command as the list user.

# Create the list (-o sets the owner)
sudo -u list mailman create -o owner@example.com team@lists.example.com

# Confirm the Postfix table was created
postmap -q team@lists.example.com hash:/var/lib/mailman3/data/postfix_lmtp

# Make Postfix reload its configuration
postfix check && postfix reload

If postmap -q returns lmtp:[127.0.0.1]:8024, the table was created correctly. Creating a list also registers administrative addresses such as team-owner@, team-request@, team-join@ and team-leave@.

Add members from a file with one address per line (- for standard input).

printf 'alice@example.com\nbob@example.net\n' | sudo -u list mailman addmembers - team@lists.example.com

# List the members
sudo -u list mailman members team@lists.example.com

# List the mailing lists
sudo -u list mailman lists

Step 5: Post and check

Send mail from a member address to the list address. Use the logs to confirm the handoff to Mailman and the delivery to members.

grep 'to=<team@lists.example.com>' /var/log/mail.log | tail -n 3
tail -n 20 /var/log/mailman3/mailman.log

If the Postfix log shows relay=127.0.0.1[127.0.0.1]:8024 and status=sent, and the Mailman log shows post to team@lists.example.com from …, the message was delivered to the members.

With the default settings, posts like the following are held instead of delivered, and the owner (the address given with -o) is notified.

Mailman log (after HOLD:) Reason
The message is not from a list member Post from an address that is not a member
Message has implicit destination The list address is not in To or Cc (for example, sent as BCC)

Approve or reject held messages and change detailed list settings in the web interface (Postorius in mailman3-full) or through the Mailman REST API.

Troubleshooting

Symptom What to check
Mail to the list is rejected with User unknown Whether you added postfix_lmtp to local_recipient_maps, ran postfix reload after creating the list, and have the list domain in mydestination
The Postfix log shows connect to 127.0.0.1[127.0.0.1]:8024: Connection refused Whether Mailman is running (systemctl status mailman3)
Posts to the list reach no one Whether the Mailman log shows HOLD:
postmap -q returns nothing The spelling of the list name and domain, and whether the address is in /var/lib/mailman3/data/postfix_lmtp

Summary

  • If you only need to forward to a small group, :include: in /etc/aliases is enough. Editing the member file takes effect immediately.
  • If you need subscriptions and posting restrictions, use Mailman 3 and integrate it by adding /var/lib/mailman3/data/postfix_lmtp to transport_maps and local_recipient_maps.
  • Mailman 2 commands such as newlist are not available on current Debian and Ubuntu.

Related articles

Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲