Ansible role to install and configure the GNU Mailing List Manager, also known as mailman.
- Python3.6 or newer with venv support
- A working mail transfer agent (MTA)
- This role can optionally create a BASIC postfix configuration
- Mailman supports postfix, exim, qmail and sendmail (strongly discouraged)
- A functioning web server
- SSL is highly recommended
- This role installs installs and configures gunicorn by default
- This role has support for nginx
- Systemd: For services and sytemd timers
- A functioning database
- Postgresql is recommended, this role only has been tested with postgresql
- See geelingguys ansible-role-postgresql for a role to setup and configure postgresql
- Root access to the machine running debian or derivates
- At least one domain for the fronted and mail server
Available variables are listed below together with their default values as specified in defaults/main.yml
.
mailman_user: "mailman"
mailman_group: "{{ mailman_user }}"
mailman_path: "/opt/mailman"
mailman_venv_path: "{{ mailman_path }}/venv"
Where to place the mailman files and configuration, who should be the owner and where to place the python virtual-env.
mailman_systemd_prefix: 'mailman-'
Prefix for the installed systemd services and timers (e.g. mailman-gunicorn.service
, mailman-notify.timer
)
mailman_apt_update: true
Update the apt-cache while installing mailman dependencies. Especially needed for older docker containers or outdated cloud images.
mailman_dependencies:
- build-essential
- sassc
- memcached
- python3-dev
- python3-wheel
- python3-setuptools
- python3-virtualenv
- memcached
- libmemcached-dev
- gettext
Apt packages that are required for mailman and the installation.
mailman_siteowner: "[email protected]"
Address of the site owner, messages that must reach a human are sent this way.
mailman_default_language: 'en'
Default language for created mailinglists
mailman_db_type: "sqlite" # pgsql, mysql, sqlite (fallback for everything)
mailman_db_name: "mailman.sqlite3"
mailman_db_user: ""
mailman_db_pass: ""
mailman_db_host: ""
Database credentials for mailman core. SQLite is the default and fallback. In production, postgresql is recommended for performance reasons.
mailman_webservice_listen: localhost
mailman_webservice_port: 8001
mailman_webservice_use_https: false
mailman_webservice_show_tracebacks: false
mailman_webservice_admin_user: 'restadmin'
mailman_webservice_admin_pass: ''
mailman_webservice_workers: 2
Configuration for the mailman web service (internal REST-API). See https://docs.mailman3.org/projects/mailman/en/latest/src/mailman/config/docs/config.html#webservice.
mailman_mta_incomming: 'mailman.mta.postfix.LMTP'
mailman_mta_outgoing: 'mailman.mta.deliver.deliver'
Incoming and outgoing MTA. Directly mapps to https://docs.mailman3.org/projects/mailman/en/latest/src/mailman/config/docs/config.html#mta
mailman_mta_smtp_host: 'localhost'
mailman_mta_smtp_port: 25
mailman_mta_smtp_user: ''
mailman_mta_smtp_pass: ''
mailman_mta_smtp_secure_mode: 'smtp'
mailman_mta_smtp_verify_cert: true
mailman_mta_smtp_verify_hostname: true
Credentials and SSL-options for SMTP.
mailman_mta_lmtp_host: '127.0.0.1'
mailman_mta_lmtp_port: 8024
Configuration for mailmans own LMTP server.
mailman_mta_max_recipients: 500
mailman_mta_max_sessions_per_connection: 0
mailman_mta_max_delivery_threads: 0
mailman_mta_delivery_retry_period: '5d'
mailman_mta_max_autoresponses_per_day: 10
Tweaks to mailmans sending behaviour.
Postorius is the django-baed web frontend for managing lists and users. It also integrates with hyperkitty as a mail archiver.
mailman_postorius_base_url: 'http{{ "s" if mailman_nginx_ssl }}://{{ mailman_nginx_server_name }}' # -> https://lists.example.org
The public URL to use in Templates and so on. Defaults to the nginx-configured values to save some typing but can be overwritten.
mailman_postorius_language_code: 'en-us'
Default language of the postorius fronted. Mapped to the LANGUAGE_CODE
setting in Django. See here for more information.
mailman_postorius_secret_key: null
Secret key for securing the application, must be set.
mailman_postorius_debug: false
mailman_postorius_siteid: 1
Debug enables stack traces on errors, the site_id specifies helps with some multi-domain setup and can normally stay 1. This stack-overflow has a good explanation what it's used for: https://stackoverflow.com/a/25468782
mailman_postorius_db_engine: 'sqlite3' # mysql, sqlite3 or postgresql_psycopg2
mailman_postorius_db_name: 'postorius.sqlite3'
mailman_postorius_db_user: ''
mailman_postorius_db_pass: ''
mailman_postorius_db_host: ''
mailman_postorius_db_port: ''
Database connectivity for postorius, directly maps to the django settings: https://docs.djangoproject.com/en/2.2/ref/settings/#std:setting-DATABASES. Postgresql (postgresql_psycopg2
) is, due to its superior performance, recommended in production. SQLite3 should only be used for testing purposes.
mailman_postorius_email_backend: 'django.core.mail.backends.smtp.EmailBackend'
mailman_postorius_email_host: '{{ mailman_mta_smtp_host }}'
mailman_postorius_email_port: '{{ mailman_mta_smtp_port }}'
mailman_postorius_email_host_user: '{{ mailman_mta_smtp_user }}'
mailman_postorius_email_host_pass: '{{ mailman_mta_smtp_pass }}'
mailman_postorius_email_default_from_email: '{{ mailman_postorius_email_host_user }}'
mailman_postorius_email_confirmation_from: '{{ mailman_postorius_email_default_from_email }}'
mailman_postorius_email_server_email: '{{ mailman_postorius_email_default_from_email }}'
mailman_postorius_email_use_ssl: true
Configuration on how postorius will send E-Mail. It uses the same MTA as mailman-core by default. See https://docs.djangoproject.com/en/2.2/topics/email/#email-backends for additional backends and how to configure them.
mailman_postorius_time_zone: '{{ ansible_date_time["tz"] }}'
The timezone used by postorius, defaults to the systems current timezone.
Note for Regions with summer-/wintertime: It is advised to set the timezone manually since ansible does not provide the location of the timezone (e.g. Europe/Berlin
) but the timezone according to the offset (CET with +01:00
in the winter and CEST with +02:00
in the summer). Therefore, you would have to re-run this role after each time change.
mailman_postorius_allowed_hosts:
- 'localhost'
- '127.0.0.1'
- '::1'
- '::ffff::127.0.0.1'
- '{{ mailman_nginx_server_name }}'
Hosts and domain names django can serve content for, if a request redirected to django does not match this list, the request will be rejected.
mailman_postorius_admins:
- name: Mailman Admin
email: mailman@localhost
Errors in the Django-Application will be sent to the listed E-Mails: https://docs.djangoproject.com/en/2.2/ref/settings/#admins
mailman_postorius_archiver_from:
- '127.0.0.1'
- '::1'
- '::ffff::127.0.0.1'
The archiver API can only be accessed from listed IPs. Since hyperkitty is running locally in this setup, it should be safe to leave unchanged.
mailman_postorius_cache_backend: 'django.core.cache.backends.memcached.PyLibMCCache'
mailman_postorius_cache_location: 'localhost'
The cache used for all sorts of django-caches. By default it usese memcached which is also installed by this role. See the django documentation for possible settings: https://docs.djangoproject.com/en/2.2/ref/settings/#std:setting-CACHES
mailman_archiver_key: 'changeme!'
Shared secret between hyperkitty and mailman for authentication. Must be set.
mailman_hyperkitty_base_url: '{{ mailman_postorius_base_url }}'
Base URL for the hyperkitty installation. Normally, there is no need to change it. But in setups with external reverse proxy it may be necessary to change it to something like 127.0.0.1:{{ mailman_gunicorn_port }}
.
mailman_hyperkitty_serach_index: false
Rebuild hyperkitty search index after each update.
Gunicorn is the WSGI HTTP server that runs postorius.
mailman_gunicorn_listen: '127.0.0.1'
mailman_gunicorn_port: 8000
IP and Port to bind to.
mailman_gunicorn_procname: '{{ mailman_systemd_prefix }}gunicorn'
Title of the process, useful for differentiating multiple gunicorn processes.
mailman_gunicorn_workers: 4
Amount of worker-processes to handle requests.
This role is able to do some basic configuration of nginx and postfix. Those configurations are not especially secure nor complete at all. It just aims to help out a bit. Be sure you know what those services are doing with your specific configuration as you don't want to host an open mail relay, really not.
mailman_postfix_setup: false
mailman_postfix_smtp_port: "smtp"
Should this role install postfix and provide the necessary configuration for mailman to work with postfix? The smtp
value of port translates to port 25
(SMTP) in postfix.
Note: This is really only recommended for testing purposes. Refer to https://docs.mailman3.org/projects/mailman/en/latest/src/mailman/docs/mta.html#transport-maps about the mailman specific configuration for postfix. The transport-maps are located at {{ mailman_path }}/var/data/{postfix_lmtp,postfix_domains}
.
mailman_nginx_enabled: false
mailman_nginx_server_name: 'lists.example.org'
mailman_nginx_ssl: true
mailman_nginx_path: '/etc/nginx/sites-enabled/mailman.conf'
mailman_nginx_ssl_redirect: '{{ mailman_nginx_ssl }}'
mailman_nginx_v6only: false
mailman_nginx_ssl_certificate: null
mailman_nginx_ssl_key: null
Set mailman_nginx_enabled
to true
in order to install a nginx configuration to the specified location and restart nginx afterwards.
Note: This role will neither install nginx nor generate SSL-Certificates from e.g. letsencrypt for you. That's on you. You can find some roles for that in the related projects section.
This is a small sample playbook utilizing most of the roles features. To run this playbook you need to have the postgresql database available. The lines ending in # Change
contain secrets which should be changed in production. Same goes for domain names, E-Mail contacts and so on.
- hosts: mailman
become: true
vars_file:
- vars/mailman.yml
roles:
- e1mo.mailman
Inside vars/mailman.yml
mailman_siteowner: '[email protected]'
mailman_db_type: 'pgsql'
mailman_db_name: 'mailman'
mailman_db_user: 'mailman'
mailman_db_pass: 'totally_secret' # Change
mailman_webservice_admin_pass: 'also_secret' # Change
mailman_mta_smtp_port: 465
mailman_mta_smtp_user: '[email protected]'
mailman_mta_smtp_pass: '1337secret42' # Change
mailman_mta_smtp_secure_mode: 'smtps'
mailman_postorius_secret_key: 'fooSecretBar' # Change
mailman_postorius_db_engine: 'postgresql_psycopg2'
mailman_postorius_db_name: 'mailman_postorius'
mailman_postorius_db_user: 'mailman_postorius'
mailman_postorius_db_pass: 'no-less-secret' # Change
mailman_postorius_db_host: 'localhost'
mailman_postorius_email_default_from_email: '[email protected]'
mailman_postorius_time_zone: 'Europe/Berlin'
mailman_postorius_admins:
- name: Fellow Hacker
email: [email protected]
mailman_archiver_key: 'not-leaking-that-one' # Change
# This is **no especially secure** postfix setup.
# Running your own, properly configured, Mailserver is highly advised.
mailman_postfix_setup: true
# You need to have nginx installed before hand
mailman_nginx_enabled: true
mailman_nginx_path: '/etc/nginc/sites-enabled/lists.mydomain.com.conf'
mailman_nginx_server_name: 'lists.mydomain.com'
mailman_nginx_ssl_certificate: '/etc/ssl/certs/ssl-cert-snakeoil.pem'
mailman_nginx_ssl_key: '/etc/ssl/private/ssl-cert-snakeoil.key'
GNU General Public License v3.0 or later. See LICENSE for the full license.
- The mailman project
- The ansible project itself, without which this role could not exist.
- ansible-role-postgresql from Jeff Geerling aka. geerlingguy which can install and configure postgresql easily.
- ansible-nginx-base and ansible-dehydrated (get letsencrypt certs) by rixx are both really simple to work with and don't get in your way. I have patched and updated both roles a fair bit, just haven't submitted the patches: ansible-nginx-base and ansible-dehydrated.
Written by Moritz 'e1mo' Fromm.
The role is developed on sourcehut at https://git.sr.ht/~e1mo/ansible-role-mailman. To contribute send your patches to ~e1mo/ansible-role-mailman [at] lists.sr.ht
using git send-email
(Mailing list etiquette). The issue-tracker is located at https://todo.sr.ht/~e1mo/ansible-role-mailman, no account needed.