diff --git a/roles/configure-unbound/README.rst b/roles/configure-unbound/README.rst
index ed986d50..9e80061f 100644
--- a/roles/configure-unbound/README.rst
+++ b/roles/configure-unbound/README.rst
@@ -29,3 +29,20 @@ usable IPv6 default route, otherwise IPv4.
    :default: 2001:4860:4860::8888 (Google)
 
    The seconary IPv6 nameserver for fowarding requests
+
+.. zuul:rolevar:: unbound_cache_max_ttl
+   :default: 86400
+
+   Maximum TTL in seconds to keep successful queries cached for.
+
+   This TTL will have precedence if the DNS record TTL is higher.
+   For example, a TTL of 90000 would be reduced to 86400.
+
+.. zuul:rolevar:: unbound_cache_min_ttl
+   :default: 0
+
+   Minimum TTL in seconds to keep queries cached for.
+   Note that this is effective for both successful and failed queries.
+
+   This TTL will have precedence if the DNS record TTL is lower.
+   For example, a TTL of 60 would be raised to 900.
diff --git a/roles/configure-unbound/defaults/main.yaml b/roles/configure-unbound/defaults/main.yaml
index 2574c60c..e67192d1 100644
--- a/roles/configure-unbound/defaults/main.yaml
+++ b/roles/configure-unbound/defaults/main.yaml
@@ -5,3 +5,20 @@ unbound_primary_nameserver_v4: "208.67.222.222"
 # Google
 unbound_secondary_nameserver_v6: "2001:4860:4860::8888"
 unbound_secondary_nameserver_v4: "8.8.8.8"
+
+# Time to live maximum for  RRsets  and  messages  in  the  cache.
+# Default  is  86400  seconds  (1  day).  If the maximum kicks in,
+# responses to clients still get decrementing TTLs  based  on  the
+# original  (larger)  values.   When the internal TTL expires, the
+# cache item has expired.  Can be set lower to force the  resolver
+# to query for data often, and not trust (very large) TTL values.
+unbound_cache_max_ttl: 86400
+
+# Time  to  live  minimum  for  RRsets  and messages in the cache.
+# Default is 0.  If the minimum kicks in, the data is  cached  for
+# longer than the domain owner intended, and thus less queries are
+# made to look up the data.  Zero makes sure the data in the cache
+# is  as the domain owner intended, higher values, especially more
+# than an hour or so, can lead to trouble as the data in the cache
+# does not match up with the actual data any more.
+unbound_cache_min_ttl: 0
diff --git a/roles/configure-unbound/tasks/main.yaml b/roles/configure-unbound/tasks/main.yaml
index 65c60a5d..64be2b8c 100644
--- a/roles/configure-unbound/tasks/main.yaml
+++ b/roles/configure-unbound/tasks/main.yaml
@@ -38,6 +38,19 @@
     unbound_primary_nameserver: '{{ unbound_primary_nameserver_v4 }}'
     unbound_secondary_nameserver: '{{ unbound_secondary_nameserver_v4 }}'
 
+- name: Include OS-specific variables
+  include_vars: "{{ item }}"
+  with_first_found:
+    - "{{ role_path }}/vars/{{ ansible_distribution }}.yaml"
+    - "{{ role_path }}/vars/{{ ansible_os_family }}.yaml"
+    - "{{ role_path }}/vars/default.yaml"
+
+- name: Ensure Unbound conf.d directory exists
+  become: yes
+  file:
+    path: "{{ unbound_confd }}"
+    state: directory
+
 # TODO: Move this to /etc/unbound/conf.d ?
 - name: Configure unbound forwarding
   become: yes
@@ -51,6 +64,18 @@
   notify:
     - Restart unbound
 
+- name: Configure unbound TTL
+  become: yes
+  template:
+    dest: "{{ unbound_confd }}/ttl.conf"
+    owner: root
+    group: root
+    mode: 0644
+    src: ttl.conf.j2
+  register: ttl_config
+  notify:
+    - Restart unbound
+
 - name: Start unbound
   become: yes
   service:
diff --git a/roles/configure-unbound/templates/ttl.conf.j2 b/roles/configure-unbound/templates/ttl.conf.j2
new file mode 100644
index 00000000..34b5881a
--- /dev/null
+++ b/roles/configure-unbound/templates/ttl.conf.j2
@@ -0,0 +1,5 @@
+# {{ ansible_managed }}
+
+server:
+    cache-min-ttl: {{ unbound_cache_min_ttl }}
+    cache-max-ttl: {{ unbound_cache_max_ttl }}
diff --git a/roles/configure-unbound/vars/Debian.yaml b/roles/configure-unbound/vars/Debian.yaml
new file mode 100644
index 00000000..ccb6146a
--- /dev/null
+++ b/roles/configure-unbound/vars/Debian.yaml
@@ -0,0 +1 @@
+unbound_confd: /etc/unbound/unbound.conf.d
diff --git a/roles/configure-unbound/vars/default.yaml b/roles/configure-unbound/vars/default.yaml
new file mode 100644
index 00000000..48bfc75a
--- /dev/null
+++ b/roles/configure-unbound/vars/default.yaml
@@ -0,0 +1 @@
+unbound_confd: /etc/unbound/conf.d
diff --git a/tests/configure-unbound.yaml b/tests/configure-unbound.yaml
index 154c598b..0fdf6cad 100644
--- a/tests/configure-unbound.yaml
+++ b/tests/configure-unbound.yaml
@@ -23,18 +23,26 @@
       assert:
         that:
           - forwarding_config | changed
+          - ttl_config | changed
 
     - name: Check if /etc/unbound/forwarding.conf exists
       stat:
         path: /etc/unbound/forwarding.conf
       register: forwarding_file
 
-    - name: Ensure that configuration file exists
+    - name: Check if /etc/unbound/conf.d/ttl.conf exists
+      stat:
+        path: "{{ unbound_confd }}/ttl.conf"
+      register: ttl_file
+
+    - name: Ensure that configuration files exist
       assert:
         that:
           - forwarding_file.stat.exists
+          - ttl_file.stat.exists
 
     # This is self-tested, no need to assert
     - name: Do a host lookup (sanity check)
       command: host openstack.org
       changed_when: false
+