Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time


... is a group email tool for small, private mailing lists. View it as one step above a long CC: list.

It's designed to integrate with an existing mail delivery agent (e.g. a Postfix installation) and was made because Postfix can almost, but not quite, do exactly what I want already.


  • Python 3.6 or above

Majordummo is implemented as a single file written in Python with no external dependencies.


  • Accepts mail
  • Resends it to a list of people
  • Drops emails if they're sent by non-members (optional)
  • Rate-limits individual posters
  • Adds Reply-To header (optional)
  • Writes messages and delivery status to the filesystem
  • Logs everything
  • That's it

Majordummo has no cronjobs or daemons, and only runs when the MDA invokes it to process mail sent to the list. It will only ever send email to the list of recipients which is set in its configuration file -- it doesn't, for example, send bounce messages. It's configured using a JSON file and has no GUI.


After creating a configuration file, create an account, or virtual alias, for the mailing list name, and set to run when mail is delivered to that account.

There are plenty of ways to do this. For example, you could "|/path/to/ --config /path/to/config" in /etc/aliases, or you could run it as part of a set of Procmail rules, or you could configure the Postfix pipe delivery agent to backend on to it, or...


Configuration is supplied by a JSON file. Majordummo has a very minimal configuration, but you will probably want to change all of it. Copy config-example.json to config.json and customise it. Fields to change:

  • recipients: The people on your list.
  • reject_non_recipients: messages from non-list members will be dropped. The sender will not be notified, but the action is logged. Recommended.
  • set_headers: A list of headers to replace (if they exist) or add (if they don't) to outgoing messages.
  • db: Location of the database used to store rate limiting information.
  • per_user_ratelimit_secs: The minimum time in seconds allowed between posts from the same user to the list. Indended to stop mail loops, not hyperactive humans.
  • archive_dir: A directory name to write emails and delivery status. Can be set to null if you don't want archives.
  • smtp: Mail server details. You will want to change mail_from.
  • logging: A Python logging configuration -- note that logging/handlers/file/filename is a filename to write logs to.

Make sure you've set archive_dir, db, and logging/handlers/file/filename to valid paths which are accessible by the user running (see "Running as a different user").

Running as a different user

Because it writes logs and archives emails, It's convenient to have majordummo run as its own user. I do this using a suid wrapper binary, but you can also do it by setting up a sudo rule and including sudo in your alias, or by using something like procmail.


When testing, it's best to set the list of recipients in your config to just yourself.

Testing can be split into two parts: 1) does my configuration do what I want? and 2) does my MDA correctly invoke majordummo?

You can test the configuration by running directly with an email:

python3 --config /path/to/config.json <email.txt

If that works, then send a test email to the list. Your MDA will log message delivery, so check those logs first. After that, check the log file you've configured in your config.json.


Simple mailing list software for small, private lists.






No releases published


No packages published