diff --git a/group_vars/all/vault_mailman.yml b/group_vars/all/vault_mailman.yml
new file mode 100644
index 0000000000000000000000000000000000000000..98cc49e1647bc5b06715c3243a8e28a5c396c44d
--- /dev/null
+++ b/group_vars/all/vault_mailman.yml
@@ -0,0 +1,9 @@
+$ANSIBLE_VAULT;1.1;AES256
+33663534393934353761333131636338343835616465386562393436663463343566376536303962
+3463363464623530653630383464326232303565333039320a386364623664613931636531336233
+34366536623139613733626539306462366139356562393166346234343261306631333763396462
+3735643430396562650a646463303232633039313037346238636239616461373337656563393239
+62386464396237303634336661363135333464363135343234626234336432333963666136346633
+66636633633237383937393431326639623938356133323566663562653964613564343231323939
+38336537656532353163366439393366373264366363303730663139383436356335613462653234
+64313365643833333739
diff --git a/playbooks/lists.archlinux.org.yml b/playbooks/lists.archlinux.org.yml
index 9829a8c3a326b4d3d02ae34acd729deca4d1d3b0..a20dea7ac0b693f11cba23590231bfe4683dcd48 100644
--- a/playbooks/lists.archlinux.org.yml
+++ b/playbooks/lists.archlinux.org.yml
@@ -11,6 +11,10 @@
     - { role: borg_client, tags: ["borg"], when: "'borg_clients' in group_names" }
     - { role: prometheus_exporters }
     - { role: promtail }
+    - { role: certbot }
+    - { role: nginx }
     - { role: fail2ban }
     - { role: rspamd, rspamd_dkim_domain: lists.archlinux.org, rspamd_dkim_use_esld: false, tags: ["mail"] }
     - { role: unbound, unbound_port: 5353, tags: ["mail"] }
+    - { role: uwsgi }
+    - { role: mailman }
diff --git a/roles/mailman/defaults/main.yml b/roles/mailman/defaults/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b2d2b3fd97fea0e40382ddc367ac056d789ad2af
--- /dev/null
+++ b/roles/mailman/defaults/main.yml
@@ -0,0 +1 @@
+lists_domain: lists.archlinux.org
diff --git a/roles/mailman/files/mailman.ini b/roles/mailman/files/mailman.ini
new file mode 100644
index 0000000000000000000000000000000000000000..fe6d040f429a53f1be93ab35c3501f5a3ba26299
--- /dev/null
+++ b/roles/mailman/files/mailman.ini
@@ -0,0 +1,10 @@
+[uwsgi]
+plugins = cgi
+socket = /run/uwsgi/%n.sock
+chmod-socket = 770
+threads = 2
+
+cgi = /=/usr/lib/mailman/cgi-bin/
+cgi-index = listinfo
+uid = mailman
+gid = http
diff --git a/roles/mailman/files/milter_header_checks b/roles/mailman/files/milter_header_checks
new file mode 100644
index 0000000000000000000000000000000000000000..0a31b0229849ad7400fe0b70a7060753df943381
--- /dev/null
+++ b/roles/mailman/files/milter_header_checks
@@ -0,0 +1,2 @@
+# We don't have a Junk folder for mailman so reject mails which are probably spam
+/^X-Spam: Yes$/ REJECT Your message has been rejected by Rspamd
diff --git a/roles/mailman/handlers/main.yml b/roles/mailman/handlers/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b48bca38207f015f2eff329e552b6dfe8908210e
--- /dev/null
+++ b/roles/mailman/handlers/main.yml
@@ -0,0 +1,6 @@
+---
+- name: reload mailman
+  service: name=mailman state=reloaded
+
+- name: reload postfix
+  service: name=postfix state=reloaded
diff --git a/roles/mailman/tasks/main.yml b/roles/mailman/tasks/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..473a670c14ae5a34c81bd087769c82736e1d7666
--- /dev/null
+++ b/roles/mailman/tasks/main.yml
@@ -0,0 +1,59 @@
+---
+- name: create ssl cert
+  include_role:
+    name: certificate
+  vars:
+    domains: ["{{ lists_domain }}"]
+
+- name: install mailman, uwsgi-plugin-cgi and postfx
+  pacman: name=mailman,uwsgi-plugin-cgi,postfix,postfix-pcre state=present
+
+- name: install mailman configuration
+  template: src=mm_cfg.py.j2 dest=/etc/mailman/mm_cfg.py follow=yes owner=root group=root mode=0644
+  notify: reload mailman
+
+- name: install postfix configuration
+  template: src=main.cf.j2 dest=/etc/postfix/main.cf owner=root group=root mode=0644
+  notify: reload postfix
+
+- name: install postfix maps
+  copy: src={{ item }} dest=/etc/postfix/ owner=root group=root mode=0644
+  loop:
+    - milter_header_checks
+
+- name: open firewall holes for postfix
+  ansible.posix.firewalld: service=smtp permanent=true state=enabled immediate=yes
+  when: configure_firewall
+  tags:
+    - firewall
+
+- name: create mailman list
+  command: /usr/lib/mailman/bin/newlist -a mailman root@{{ lists_domain }} meG0n5Wq6dEWCA6s
+  args:
+    creates: /var/lib/mailman/lists/mailman
+
+- name: configure mailman uwsgi service
+  copy: src=mailman.ini dest=/etc/uwsgi/vassals/ owner=mailman group=http mode=0644
+
+- name: make nginx log dir
+  file: path=/var/log/nginx/{{ lists_domain }} state=directory owner=root group=root mode=0755
+
+- name: set up nginx
+  template: src=nginx.d.conf.j2 dest="/etc/nginx/nginx.d/mailman.conf" owner=root group=root mode=644
+  notify: reload nginx
+  tags: ['nginx']
+
+- name: start and enable postfix
+  systemd: name=postfix.service enabled=yes daemon_reload=yes state=started
+
+- name: start and enable mailman{.service,-*.timer}
+  systemd: name={{ item }} enabled=yes daemon_reload=yes state=started
+  loop:
+    - mailman.service
+    - mailman-senddigests.timer
+    - mailman-nightlygzip.timer
+    - mailman-mailpasswds.timer
+    - mailman-gatenews.timer
+    - mailman-disabled.timer
+    - mailman-cullbadshunt.timer
+    - mailman-checkdbs.timer
diff --git a/roles/mailman/templates/main.cf.j2 b/roles/mailman/templates/main.cf.j2
new file mode 100644
index 0000000000000000000000000000000000000000..f14357e607f584a8df514f103db1310c7975dae3
--- /dev/null
+++ b/roles/mailman/templates/main.cf.j2
@@ -0,0 +1,50 @@
+#
+# {{ansible_managed}}
+#
+
+compatibility_level = 3.6
+
+biff = no
+smtputf8_enable = no
+
+smtpd_tls_cert_file = /etc/letsencrypt/live/{{ lists_domain }}/fullchain.pem
+smtpd_tls_key_file = /etc/letsencrypt/live/{{ lists_domain }}/privkey.pem
+smtpd_tls_loglevel = 1
+smtpd_tls_security_level = may
+
+smtp_tls_loglevel = 1
+smtp_tls_security_level = may
+
+mydomain = {{ lists_domain }}
+myorigin = {{ lists_domain }}
+mydestination = {{ lists_domain }}
+
+# fatal: configuration error: mailbox_size_limit is smaller than message_size_limit
+message_size_limit = 104857600
+mailbox_size_limit = $message_size_limit
+recipient_delimiter = +
+disable_vrfy_command = yes
+strict_rfc821_envelopes = yes
+
+# enable for testing new config
+soft_bounce = no
+debug_peer_list =
+
+smtpd_relay_restrictions =
+    permit_mynetworks
+    permit_sasl_authenticated
+    reject_unauth_destination
+
+smtpd_reject_footer = For assistance contact <postmaster@archlinux.org>. Please provide the following information in your problem report: time ($localtime) and client ($client_address).
+
+# rspamd
+smtpd_milters = inet:localhost:11332
+non_smtpd_milters = $smtpd_milters
+
+alias_maps = hash:/var/lib/mailman/data/aliases
+alias_database = $alias_maps
+virtual_alias_maps = hash:/var/lib/mailman/data/virtual-mailman
+
+milter_header_checks = pcre:/etc/postfix/milter_header_checks
+
+delay_warning_time = 4h
diff --git a/roles/mailman/templates/mm_cfg.py.j2 b/roles/mailman/templates/mm_cfg.py.j2
new file mode 100644
index 0000000000000000000000000000000000000000..abe99fe3098dbe0cb18f20f8c34b7ac23ba402c5
--- /dev/null
+++ b/roles/mailman/templates/mm_cfg.py.j2
@@ -0,0 +1,79 @@
+# -*- python -*-
+
+# Copyright (C) 1998-2018 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software 
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+"""This module contains your site-specific settings.
+
+From a brand new distribution it should be copied to mm_cfg.py.  If you
+already have an mm_cfg.py, be careful to add in only the new settings you
+want.  Mailman's installation procedure will never overwrite your mm_cfg.py
+file.
+
+The complete set of distributed defaults, with documentation, are in the file
+Defaults.py.  In mm_cfg.py, override only those you want to change, after the
+
+  from Defaults import *
+
+line (see below).
+
+Note that these are just default settings; many can be overridden via the
+administrator and user interfaces on a per-list or per-user basis.
+
+Also note that many of these settings will not be effective until Mailman
+is restarted.  Thus, you should always restart Mailman after changing this
+file.
+
+Further, settings which relate to a list's host_name and web_page_url only
+affect lists created after the change.  For existing lists, see the FAQ at
+<http://wiki.list.org/x/mIA9>.
+
+"""
+
+###############################################
+# Here's where we get the distributed defaults.
+
+from Defaults import *
+
+##################################################
+# Put YOUR site-specific settings below this line.
+
+# Please see: http://wiki.list.org/x/mIA9 if you change this
+DEFAULT_URL_HOST = '{{ lists_domain }}'
+DEFAULT_EMAIL_HOST = '{{ lists_domain }}'
+MTA = 'Postfix'
+
+VIRTUAL_HOSTS.clear()
+add_virtualhost(DEFAULT_URL_HOST, DEFAULT_EMAIL_HOST)
+
+POSTFIX_STYLE_VIRTUAL_DOMAINS = ['{{ lists_domain }}']
+
+DEFAULT_URL_PATTERN = 'https://%s/'
+PUBLIC_ARCHIVE_URL = 'https://%(hostname)s/pipermail/%(listname)s'
+
+# bot protection
+SUBSCRIBE_FORM_SECRET = '{{ vault_mailman_subscribe_form_secret }}'
+
+VIRTUAL_HOST_OVERVIEW = Off
+
+DEFAULT_SEND_REMINDERS = 0
+
+PUBLIC_MBOX = Yes
+
+DEFAULT_MSG_HEADER = ""
+DEFAULT_MSG_FOOTER = ""
+#DEFAULT_DMARC_MODERATION_ACTION = 1
+REMOVE_DKIM_HEADERS = 1
diff --git a/roles/mailman/templates/nginx.d.conf.j2 b/roles/mailman/templates/nginx.d.conf.j2
new file mode 100644
index 0000000000000000000000000000000000000000..accb7e83bc3d64123526798f4e0629458f98dcc2
--- /dev/null
+++ b/roles/mailman/templates/nginx.d.conf.j2
@@ -0,0 +1,53 @@
+server {
+    listen       80;
+    listen       [::]:80;
+    server_name  {{ lists_domain }};
+
+    access_log   /var/log/nginx/{{ lists_domain }}/access.log main;
+    access_log   /var/log/nginx/{{ lists_domain }}/access.log.json json_main;
+    error_log    /var/log/nginx/{{ lists_domain }}/error.log;
+
+    include snippets/letsencrypt.conf;
+
+    location / {
+        access_log off;
+        return 301 https://$server_name$request_uri;
+    }
+}
+
+server {
+    listen       443 ssl http2;
+    listen       [::]:443 ssl http2;
+    server_name  {{ lists_domain }};
+
+    access_log   /var/log/nginx/{{ lists_domain }}/access.log main;
+    access_log   /var/log/nginx/{{ lists_domain }}/access.log.json json_main;
+    error_log    /var/log/nginx/{{ lists_domain }}/error.log;
+
+    ssl_certificate      /etc/letsencrypt/live/{{ lists_domain }}/fullchain.pem;
+    ssl_certificate_key  /etc/letsencrypt/live/{{ lists_domain }}/privkey.pem;
+    ssl_trusted_certificate /etc/letsencrypt/live/{{ lists_domain }}/chain.pem;
+
+    # redirect old urls
+    location /mailman {
+        rewrite ^/mailman/(.*) /$1 permanent;
+    }
+
+    location /icons {
+        alias /usr/lib/mailman/icons;
+    }
+
+    location /pipermail {
+        alias /var/lib/mailman/archives/public;
+        autoindex on;
+    }
+
+    location / {
+        root            /usr/lib/mailman/cgi-bin/;
+        index           listinfo;
+        include         uwsgi_params;
+        uwsgi_modifier1 9;
+        uwsgi_pass      unix:/run/uwsgi/mailman.sock;
+    }
+
+}