diff --git a/ansible/certificates.yml b/ansible/certificates.yml
index 28a1a5f8ee1cd400e3ea63c16be394781751efa3..772aa2f5c3c999a4a624b064fa2a56cf04bcb7b8 100644
--- a/ansible/certificates.yml
+++ b/ansible/certificates.yml
@@ -2,7 +2,8 @@
 - import_playbook: gather-facts.yml
   when: >-
     kolla_enable_tls_backend | default(false) | bool or
-    rabbitmq_enable_tls | default(false) | bool
+    rabbitmq_enable_tls | default(false) | bool or
+    certificates_generate_libvirt | default(libvirt_tls) | default(false) | bool
 
 - name: Apply role certificates
   hosts: localhost
diff --git a/ansible/roles/certificates/defaults/main.yml b/ansible/roles/certificates/defaults/main.yml
index a478311effc029a3f8a3fc051e585dfce4594cc9..473bbb40d4486c9d2aa4b1383f859b9d167e2429 100644
--- a/ansible/roles/certificates/defaults/main.yml
+++ b/ansible/roles/certificates/defaults/main.yml
@@ -3,3 +3,9 @@ root_dir: "{{ kolla_certificates_dir }}/private/root"
 external_dir: "{{ kolla_certificates_dir }}/private/external"
 internal_dir: "{{ kolla_certificates_dir }}/private/internal"
 backend_dir: "{{ kolla_certificates_dir }}/private/backend"
+libvirt_dir: "{{ kolla_certificates_dir }}/private/libvirt"
+
+# Whether to generate certificates for libvirt TLS.
+certificates_generate_libvirt: "{{ libvirt_tls | default(false) | bool }}"
+# Directory into which to copy generated certificates and keys for libvirt TLS.
+certificates_libvirt_output_dir: "{{ node_custom_config }}/nova/nova-libvirt"
diff --git a/ansible/roles/certificates/tasks/generate-libvirt.yml b/ansible/roles/certificates/tasks/generate-libvirt.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c6551275065317a4a9b66388c5c49b329c01f950
--- /dev/null
+++ b/ansible/roles/certificates/tasks/generate-libvirt.yml
@@ -0,0 +1,84 @@
+---
+- name: Ensuring private libvirt directory exist
+  file:
+    path: "{{ libvirt_dir }}"
+    state: "directory"
+    mode: "0770"
+
+- name: Creating libvirt SSL configuration file
+  template:
+    src: "{{ item }}.j2"
+    dest: "{{ kolla_certificates_dir }}/{{ item }}"
+    mode: "0660"
+  with_items:
+    - "openssl-kolla-libvirt.cnf"
+
+- name: Creating libvirt certificate key
+  command: >
+    openssl genrsa
+    -out "{{ libvirt_dir }}/libvirt.key" 2048
+  args:
+    creates: "{{ libvirt_dir }}/libvirt.key"
+
+- name: Creating libvirt certificate signing request
+  command: >
+    openssl req
+    -new
+    -key "{{ libvirt_dir }}/libvirt.key"
+    -out "{{ libvirt_dir }}/libvirt.csr"
+    -config "{{ kolla_certificates_dir }}/openssl-kolla-libvirt.cnf"
+    -sha256
+  args:
+    creates: "{{ libvirt_dir }}/libvirt.csr"
+
+- name: Creating libvirt certificate
+  command: >
+    openssl x509
+    -req
+    -in "{{ libvirt_dir }}/libvirt.csr"
+    -CA "{{ root_dir }}/root.crt"
+    -CAkey "{{ root_dir }}/root.key"
+    -CAcreateserial
+    -extensions v3_req
+    -extfile "{{ kolla_certificates_dir }}/openssl-kolla-libvirt.cnf"
+    -out "{{ libvirt_dir }}/libvirt.crt"
+    -days 500
+    -sha256
+  args:
+    creates: "{{ libvirt_dir }}/libvirt.crt"
+
+- name: Setting permissions on libvirt key
+  file:
+    path: "{{ libvirt_dir }}/libvirt.key"
+    mode: "0660"
+    state: file
+
+- name: Ensure libvirt output directory exists
+  file:
+    path: "{{ certificates_libvirt_output_dir }}"
+    state: directory
+    mode: "0770"
+
+- name: Copy libvirt root CA to default configuration location
+  copy:
+    src: "{{ root_dir }}/root.crt"
+    dest: "{{ certificates_libvirt_output_dir }}/cacert.pem"
+    mode: "0660"
+
+- name: Copy libvirt cert to default configuration locations
+  copy:
+    src: "{{ libvirt_dir }}/libvirt.crt"
+    dest: "{{ certificates_libvirt_output_dir }}/{{ item }}cert.pem"
+    mode: "0660"
+  loop:
+    - server
+    - client
+
+- name: Copy libvirt key to default configuration locations
+  copy:
+    src: "{{ libvirt_dir }}/libvirt.key"
+    dest: "{{ certificates_libvirt_output_dir }}/{{ item }}key.pem"
+    mode: "0660"
+  loop:
+    - server
+    - client
diff --git a/ansible/roles/certificates/tasks/main.yml b/ansible/roles/certificates/tasks/main.yml
index f8d80dd9b98845c76c4b82cfd37a798a3be332b8..eccd6d668de07411624108dc231475c7ee4cd975 100644
--- a/ansible/roles/certificates/tasks/main.yml
+++ b/ansible/roles/certificates/tasks/main.yml
@@ -4,3 +4,5 @@
 - include_tasks: generate-backend.yml
   when:
     - kolla_enable_tls_backend | bool or rabbitmq_enable_tls | bool
+- include_tasks: generate-libvirt.yml
+  when: certificates_generate_libvirt | bool
diff --git a/ansible/roles/certificates/templates/openssl-kolla-libvirt.cnf.j2 b/ansible/roles/certificates/templates/openssl-kolla-libvirt.cnf.j2
new file mode 100644
index 0000000000000000000000000000000000000000..f625e03a4adb0980d1f62dc07b2b2a8c62588bdb
--- /dev/null
+++ b/ansible/roles/certificates/templates/openssl-kolla-libvirt.cnf.j2
@@ -0,0 +1,18 @@
+[req]
+prompt = no
+distinguished_name = req_distinguished_name
+req_extensions = v3_req
+
+[req_distinguished_name]
+countryName = US
+stateOrProvinceName = NC
+localityName = RTP
+organizationalUnitName = kolla
+
+[v3_req]
+subjectAltName = @alt_names
+
+[alt_names]
+{% for host in groups['compute'] %}
+DNS.{{ loop.index }} = {{ hostvars[host].migration_hostname | default(hostvars[host].ansible_facts.nodename) }}
+{% endfor %}
diff --git a/releasenotes/notes/libvirt-tls-certificates-b4b27b1a6b4a2db7.yaml b/releasenotes/notes/libvirt-tls-certificates-b4b27b1a6b4a2db7.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..8ee99dd7e001d33b60dff70990aca4ce423b4e74
--- /dev/null
+++ b/releasenotes/notes/libvirt-tls-certificates-b4b27b1a6b4a2db7.yaml
@@ -0,0 +1,10 @@
+---
+features:
+  - |
+    Adds support to the ``kolla-ansible certificates`` command for generating
+    certificates for libvirt TLS, when ``libvirt_tls`` is ``true``. The same
+    certificate and key are used for the libvirt client and server.
+
+    The certificates use the same root CA as the other generated certificates,
+    and are written to ``{{ node_custom_config }}/nova/nova-libvirt/``, ready
+    to be picked up by nova-libvirt and nova-compute.