diff --git a/docs/services/system.md b/docs/services/system.md
index bfd4d0e..325ce82 100644
--- a/docs/services/system.md
+++ b/docs/services/system.md
@@ -64,3 +64,27 @@ system_security_ssh_unauthorizedkeys: [] # list of unauthorized/revoked public k
 [Default configuration](https://gitlab.com/etke.cc/roles/ssh/-/blob/main/defaults/main.yml) is good enough as-is, but we strongly suggest you to **verify everything before applying any changes!**, otherwise you may lock yourself out.
 
 There are various of different configuration options - check the defaults and adjust them to your needs.
+
+### fail2ban
+
+To enable [fail2ban](https://fail2ban.org/wiki/index.php/Main_Page) installation, management and integration with SSHd, add the following configuration to your `vars.yml` file and re-run the [installation](../installing.md) process:
+
+```yaml
+########################################################################
+#                                                                      #
+# system                                                               #
+#                                                                      #
+########################################################################
+
+system_security_fail2ban_enabled: true
+system_security_fail2ban_sshd_port: 22
+# if you decided to use the playbook-managed ssh described above,
+# you can replace the line above with the following:
+# system_security_fail2ban_sshd_port: "{{ system_security_ssh_port }}"
+
+########################################################################
+#                                                                      #
+# /system                                                              #
+#                                                                      #
+########################################################################
+```
diff --git a/requirements.yml b/requirements.yml
index e666944..85df2f6 100644
--- a/requirements.yml
+++ b/requirements.yml
@@ -9,6 +9,9 @@
 - src: git+https://gitlab.com/etke.cc/roles/ssh
   version: 7458ce11f26822427eb5dc7356e59b3bd2399f7b
 
+- src: git+https://gitlab.com/etke.cc/roles/fail2ban
+  version: 93f31e7153c521c8987091313a30938d6ae35337
+
 - src: git+https://github.com/devture/com.devture.ansible.role.docker_sdk_for_python.git
   version: 129c8590e106b83e6f4c259649a613c6279e937a
 
diff --git a/setup.yml b/setup.yml
index 8c329d5..96d3257 100644
--- a/setup.yml
+++ b/setup.yml
@@ -41,6 +41,8 @@
 
     - role: galaxy/ssh
 
+    - role: galaxy/fail2ban
+
     # This role exposes various tags (setup-postgres, setup-all, upgrade-postgres, import-postgres, etc.), so we don't tag it here.
     - role: galaxy/com.devture.ansible.role.postgres