diff --git a/request-tracker5/defaults/main.yml b/request-tracker5/defaults/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0085322e43ff43575b93ba5a7576a292e4dfaeec
--- /dev/null
+++ b/request-tracker5/defaults/main.yml
@@ -0,0 +1,5 @@
+---
+
+rt_ldap_password: "{{ lookup('passwordstore', rt_ldappass) }}"
+rt_disallowexecutecode: true
+rt_configure_caddy: false
diff --git a/request-tracker5/files/crontab b/request-tracker5/files/crontab
new file mode 100644
index 0000000000000000000000000000000000000000..a15abb7a1160dd6a6808e6af3bab423785908901
--- /dev/null
+++ b/request-tracker5/files/crontab
@@ -0,0 +1,5 @@
+0 0 * * * www-data [ -x /usr/sbin/rt-email-digest-5 ] && /usr/sbin/rt-email-digest-5 -m daily
+0 0 * * 0 www-data [ -x /usr/sbin/rt-email-digest-5 ] && /usr/sbin/rt-email-digest-5 -m weekly
+0 * * * * www-data [ -x /usr/sbin/rt-email-dashboards-5 ] && /usr/sbin/rt-email-dashboards-5
+# Enable the following cron job if you have set up external storage
+0 0 * * * www-data [ -x /usr/sbin/rt-externalize-attachments-5 ] && /usr/sbin/rt-externalize-attachments-5
diff --git a/request-tracker5/handlers/main.yml b/request-tracker5/handlers/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0161dfa364c1dacbc845872373e8a4c970dee7f7
--- /dev/null
+++ b/request-tracker5/handlers/main.yml
@@ -0,0 +1,14 @@
+---
+
+- name: Reload systemd
+  systemd:
+    daemon_reload: true
+
+- name: dpkg-reconfigure request-tracker5
+  command: >-
+    dpkg-reconfigure --frontend=noninteractive --priority=low request-tracker5
+
+- name: Restart RT5
+  systemd:
+    name: request-tracker5.service
+    state: restarted
diff --git a/request-tracker5/tasks/main.yml b/request-tracker5/tasks/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b7b56d89df24bbfe7ad2bf6c35aceaa26d0b2743
--- /dev/null
+++ b/request-tracker5/tasks/main.yml
@@ -0,0 +1,114 @@
+---
+
+- name: Preseed RT5 configuration
+  debconf:
+    name: request-tracker5
+    question: "request-tracker5/{{ item.question }}"
+    value: "{{ item.value }}"
+    vtype: "{{ item.vtype | default('string') }}"
+  loop:
+    - question: handle-siteconfig-permissions
+      vtype: boolean
+      value: true
+    - question: database-type
+      vtype: select
+      value: pgsql
+    - question: dbconfig-install
+      vtype: boolean
+      value: true
+    - question: install-cronjobs
+      vtype: boolean
+      value: true
+  notify:
+    - dpkg-reconfigure request-tracker5
+  tags:
+    - config
+
+- name: Install RT5
+  apt:
+    name:
+      - request-tracker5
+      - rt5-db-postgresql
+      - rt5-standalone
+    state: present
+    install_recommends: false
+
+- name: Configure RT5
+  template:
+    src: "{{ item }}"
+    dest: /etc/request-tracker5/RT_SiteConfig.d/{{ item }}
+    owner: root
+    group: www-data
+    mode: "0640"
+  loop:
+    - 50-debconf.pm
+    - 90-ansible.pm
+  notify:
+    - dpkg-reconfigure request-tracker5  # to keep debconf in sync
+    - Restart RT5
+  tags:
+    - config
+
+- name: Fix RT5 config file permissions
+  file:
+    path: /etc/request-tracker5/{{ item }}
+    owner: root
+    group: www-data
+    mode: "0640"
+  loop:
+    - RT_SiteConfig.pm
+    - RT_SiteConfig.d/50-debconf.pm
+    - RT_SiteConfig.d/51-dbconfig-common.pm
+  notify:
+    - dpkg-reconfigure request-tracker5  # to keep debconf in sync
+    - Restart RT5
+  tags:
+    - config
+
+- name: Create directory for attachment storage
+  file:
+    path: /var/lib/request-tracker5/attachments
+    state: directory
+    owner: www-data
+    group: www-data
+    mode: "02750"
+
+- name: Install crontab
+  copy:
+    src: crontab
+    dest: /etc/cron.d/request-tracker5
+    owner: root
+    group: root
+    mode: "0755"  # wrong, but the package does it this way ¯\_(ツ)_/¯
+
+- name: Install RT5 systemd service
+  template:
+    src: request-tracker5.service
+    dest: /etc/systemd/system/request-tracker5.service
+    owner: root
+    group: root
+    mode: "0644"
+  notify:
+    - Reload systemd
+    - Restart RT5
+  tags:
+    - config
+
+- name: Configure Caddy
+  include_role:
+    name: webservices/caddy
+    tasks_from: site
+  vars:
+    site_name: "{{ rt_webdomain }}"
+    site_config:
+      root *: /usr/share/request-tracker5/html
+      reverse_proxy *: unix//run/request-tracker5/request-tracker5.sock
+  when: rt_configure_caddy
+
+- meta: flush_handlers
+
+- name: Enable and start RT5
+  systemd:
+    name: request-tracker5.service
+    state: started
+    enabled: true
diff --git a/request-tracker5/templates/50-debconf.pm b/request-tracker5/templates/50-debconf.pm
new file mode 100644
index 0000000000000000000000000000000000000000..80453c67501d0ce01c1974bfcd1b756888a54681
--- /dev/null
+++ b/request-tracker5/templates/50-debconf.pm
@@ -0,0 +1,14 @@
+{#- DO NOT add other configuration options to this file, keep in sync with Debian’s debconf template! -#}
+# THE BASICS:
+
+Set($rtname, '{{ rt_name }}');
+Set($Organization, '{{ rt_organization }}');
+
+Set($CorrespondAddress , '{{ rt_correspondaddress }}');
+Set($CommentAddress , '{{ rt_commentaddress }}');
+
+# THE WEBSERVER:
+
+Set($WebPath , "");
+Set($WebBaseURL , "https://{{ rt_webdomain }}");
+
diff --git a/request-tracker5/templates/90-ansible.pm b/request-tracker5/templates/90-ansible.pm
new file mode 100644
index 0000000000000000000000000000000000000000..b70b69ccc96d89de9568083738db4942c91d345c
--- /dev/null
+++ b/request-tracker5/templates/90-ansible.pm
@@ -0,0 +1,95 @@
+{{ ansible_managed | comment }}
+
+### Web
+Set($MessageBoxRichText, 0);
+Set($PreferRichText, 0);
+Set($PlainTextMono, 1);
+Set($AllowLoginPasswordAutoComplete, 1);
+Set($DisallowExecuteCode, {{ rt_disallowexecutecode|int }});
+
+### Web
+Set($WebDomain, '{{ rt_webdomain }}');
+Set($CanonicalizeRedirectURLs, 1);
+Set($CanonicalizeURLsInFeeds, 1);
+Set($WebPort, '443');
+
+### E-Mail
+Set($ParseNewMessageForTicketCcs, 1);
+Set($SetOutgoingMailFrom, '{{ rt_bounceaddress }}');
+Set($UseFriendlyToLine, 1);
+Set($NotifyActor, 1);
+
+### Logo
+Set($LogoAltText, '{{ rt_logotext }}');
+Set($LogoLinkURL, '{{ rt_logolinkurl }}');
+#Set($LogoURL, it’s easier to upload this in the web interface);
+
+### LDAP
+Set($LDAPHost,'{{ rt_ldaphost }}');
+Set($LDAPUser,'{{ rt_ldapuser }}');
+Set($LDAPPassword,'{{ rt_ldap_password }}');
+Set($LDAPBase,'{{ rt_ldapbase }}');
+Set($LDAPFilter, '(&(objectClass=user)(!(objectClass=computer))(uidNumber=*)(unixHomeDirectory=*)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))');
+Set($LDAPOptions, [raw => qr/(displayName)/]);
+Set($LDAPUpdateUsers, 1);
+Set($LDAPMapping, {Name         => 'sAMAccountName', # required
+                   EmailAddress => 'mail',
+                   RealName     => sub {
+                     use Encode qw(decode);
+                     my %args = @_;
+                     my @values = grep defined && length, $args{'ldap_entry'}->get_value('displayName');
+                     foreach my $v (@values) {
+                       $v = decode('UTF-8', $v);
+                     }
+                     return @values;
+                   },
+                   WorkPhone    => 'telephoneNumber',
+                   Organization => 'departmentName'});
+
+
+# If you want to sync Groups from LDAP into RT
+
+Set($LDAPGroupBase, '{{ rt_ldapbase }}');
+Set($LDAPGroupFilter, '(&(objectclass=group)(|{{ rt_ldapgroups }}))');
+Set($LDAPGroupMapping, {Name               => 'cn',
+                        Member_Attr        => 'member',
+                        Member_Attr_Value  => 'dn',
+                        Description        => 'description'});
+
+
+### Login
+Set($ExternalAuthPriority, [ 'My_LDAP' ]);
+Set($ExternalInfoPriority, [ 'My_LDAP' ]);
+Set($AutoCreateNonExternalUsers, 1);
+Set($ExternalSettings, {
+        'My_LDAP' => {
+                'type' => 'ldap',
+                'server' => '{{ rt_ldaphost }}',
+                'user' => '{{ rt_ldapuser }}',
+                'pass' => '{{ rt_ldap_password }}',
+                'base' => '{{ rt_ldapbase }}',
+                'filter' => '(&(objectClass=user)(!(objectClass=computer))(uidNumber=*)(unixHomeDirectory=*)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))',
+                'attr_match_list' => ['Name', 'EmailAddress'],
+                'attr_map' => {
+                        'Name' => 'sAMAccountName',
+                        'EmailAddress' => 'mail',
+                        'RealName' => sub {
+                          use Encode qw(decode);
+                          my %args = @_;
+                          my @values = grep defined && length,
+                            $args{'external_entry'}->get_value('displayName');
+                          foreach my $v (@values) {
+                            $v = decode('UTF-8', $v);
+                          }
+                          return @values;
+                        },
+                },
+                'net_ldap_args' => [raw => qr/(displayName)/],
+        },
+});
+
+### External storage
+Set(%ExternalStorage,
+    Type => 'Disk',
+    Path => '/var/lib/request-tracker5/attachments',
+);
diff --git a/request-tracker5/templates/request-tracker5.service b/request-tracker5/templates/request-tracker5.service
new file mode 100644
index 0000000000000000000000000000000000000000..7b5fde8f9de010a1983aa32f5529af00472d2176
--- /dev/null
+++ b/request-tracker5/templates/request-tracker5.service
@@ -0,0 +1,19 @@
+[Unit]
+Description=Request Tracker 5
+After=postgresql.service
+Wants=postgresql.service
+
+[Service]
+User=www-data
+Group=www-data
+Restart=on-failure
+RuntimeDirectory=%N
+UMask=0002
+ExecStart=/usr/bin/plackup \
+	-E deployment \
+	-s Starlet \
+	--socket ${RUNTIME_DIRECTORY}/%N.sock \
+	-a /usr/share/request-tracker5/libexec/rt-server
+
+[Install]
+WantedBy=multi-user.target