diff --git a/opensmtpd-relay/defaults/main.yml b/opensmtpd-relay/defaults/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b90fcc8ea1d0aedf04b9d2a557d6a18c08445dbc
--- /dev/null
+++ b/opensmtpd-relay/defaults/main.yml
@@ -0,0 +1,6 @@
+---
+
+opensmtpd_system_user_max: 999
+mailname: example.org
+opensmtpd_relayhost: mail.example.org
+adminaddr: admin@{{ mailname }}
diff --git a/opensmtpd-relay/handlers/main.yml b/opensmtpd-relay/handlers/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..93eb87ba453ded3f1a48689499a5275a1f13ee83
--- /dev/null
+++ b/opensmtpd-relay/handlers/main.yml
@@ -0,0 +1,6 @@
+---
+
+- name: Restart OpenSMTPD
+  systemd:
+    name: opensmtpd.service
+    state: restarted
diff --git a/opensmtpd-relay/tasks/main.yml b/opensmtpd-relay/tasks/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..901eeaf48576e396d9f5cfdf0c8ba1004e6d8ccc
--- /dev/null
+++ b/opensmtpd-relay/tasks/main.yml
@@ -0,0 +1,56 @@
+---
+
+- name: Install OpenSMTPD and required tools
+  package:
+    name:
+      - opensmtpd
+      - moreutils  # sponge, for update-opensmtpd-system-user
+    state: present
+
+- name: Remove esmtp
+  package:
+    name: esmtp
+    state: absent
+
+- name: Configure mailname
+  copy:
+    content: "{{ mailname }}\n"
+    dest: /etc/{{ "opensmtpd/" if is_rhel }}mailname
+    owner: root
+    group: root
+    mode: "0644"
+  notify:
+    - Restart OpenSMTPD
+
+- name: Configure smtpd.conf
+  template:
+    src: smtpd.conf.j2
+    dest: /etc/{{ "opensmtpd/" if is_rhel }}smtpd.conf
+    owner: root
+    group: root
+    mode: "0644"
+    validate: /usr/sbin/smtpd -f %s -n
+  notify:
+    - Restart OpenSMTPD
+
+- name: Install update-opensmtpd-system-user script
+  template:
+    src: update-opensmtpd-system-user.j2
+    dest: /usr/local/sbin/update-opensmtpd-system-user
+    owner: root
+    group: root
+    mode: "0754"
+
+- name: Configure system-user table cronjob
+  cron:
+    name: opensmtpd-system-user
+    cron_file: opensmtpd-system-user
+    user: root
+    minute: "*/5"
+    job: /usr/local/sbin/update-opensmtpd-system-user
+
+- name: Enable and start OpenSMTPD
+  systemd:
+    name: opensmtpd.service
+    state: started
+    enabled: true
diff --git a/opensmtpd-relay/templates/smtpd.conf.j2 b/opensmtpd-relay/templates/smtpd.conf.j2
new file mode 100644
index 0000000000000000000000000000000000000000..1befe84bd64164f9b60ddaac0acef1a2fb5f682f
--- /dev/null
+++ b/opensmtpd-relay/templates/smtpd.conf.j2
@@ -0,0 +1,9 @@
+table systemusers file:/etc/opensmtpd/systemusers
+filter "local-system-user" phase rcpt-to match rcpt-to <systemusers> rewrite "<{{ adminaddr }}>"
+
+listen on localhost filter "local-system-user"
+listen on socket filter "local-system-user"
+
+action "relay" relay host "{{ opensmtpd_relayhost }}" helo "{{ ansible_fqdn }}"
+
+match from local for any action "relay"
diff --git a/opensmtpd-relay/templates/update-opensmtpd-system-user.j2 b/opensmtpd-relay/templates/update-opensmtpd-system-user.j2
new file mode 100644
index 0000000000000000000000000000000000000000..faf9756149836a6b7d90e37eede83c2bcc5b56e0
--- /dev/null
+++ b/opensmtpd-relay/templates/update-opensmtpd-system-user.j2
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+set -e
+set -o pipefail
+
+getent passwd | awk -F: \
+	'$3 <= {{ opensmtpd_system_user_max }} { print $1 "@{{ mailname }}" }' \
+	| sponge /etc/{{ "opensmtpd/" if is_rhel }}systemusers