diff --git a/ansible/kolla-ansible.yml b/ansible/kolla-ansible.yml
index 854fdd30d9b52e2f2c2458c4e821743d8fc1271c..cc7fcd1d6e51712332fb753486171211d39da960 100644
--- a/ansible/kolla-ansible.yml
+++ b/ansible/kolla-ansible.yml
@@ -290,6 +290,7 @@
         kolla_external_fqdn_cert: "{{ kolla_config_path }}/certificates/haproxy.pem"
         kolla_internal_fqdn_cert: "{{ kolla_config_path }}/certificates/haproxy-internal.pem"
         kolla_ansible_passwords_path: "{{ kayobe_config_path }}/kolla/passwords.yml"
+        kolla_overcloud_group_vars_path: "{{ kayobe_config_path }}/kolla/inventory/group_vars"
         # NOTE: This differs from the default SELinux mode in kolla ansible,
         # which is permissive. The justification for using this mode is twofold:
         # 1. it avoids filling up the audit log
diff --git a/ansible/roles/kolla-ansible/defaults/main.yml b/ansible/roles/kolla-ansible/defaults/main.yml
index 2294dc67945b433f54787cae210d70ff6709d53b..e49ce365c068fe93c21d4e3c27de712b36b6987f 100644
--- a/ansible/roles/kolla-ansible/defaults/main.yml
+++ b/ansible/roles/kolla-ansible/defaults/main.yml
@@ -54,6 +54,9 @@ kolla_ansible_become: true
 # Full custom seed inventory contents.
 kolla_seed_inventory_custom:
 
+# Directory containing custom Kolla-Ansible group vars.
+kolla_overcloud_group_vars_path:
+
 # List of names of host variables to pass through from kayobe hosts to
 # the kolla-ansible seed host, if set. See also
 # kolla_seed_inventory_pass_through_host_vars_map.
diff --git a/ansible/roles/kolla-ansible/tasks/config.yml b/ansible/roles/kolla-ansible/tasks/config.yml
index 57b73065bc668f08f0fb411d62abad9e6051f225..151c1dd943440f6191d8a0c16b96b44c56e08252 100644
--- a/ansible/roles/kolla-ansible/tasks/config.yml
+++ b/ansible/roles/kolla-ansible/tasks/config.yml
@@ -38,6 +38,7 @@
     - "{{ kolla_config_path }}"
     - "{{ kolla_seed_inventory_path }}/host_vars"
     - "{{ kolla_overcloud_inventory_path }}/host_vars"
+    - "{{ kolla_overcloud_inventory_path }}/group_vars"
     - "{{ kolla_node_custom_config_path }}"
 
 - name: Ensure the Kolla global configuration file exists
@@ -72,6 +73,17 @@
     dest: "{{ kolla_overcloud_inventory_path }}/hosts"
     mode: 0640
 
+- name: Look for custom Kolla overcloud group vars
+  stat:
+    path: "{{ kolla_overcloud_group_vars_path }}"
+  register: kolla_ansible_custom_overcloud_group_vars
+
+- name: Copy over custom Kolla overcloud group vars
+  copy:
+    src: "{{ kolla_overcloud_group_vars_path }}"
+    dest: "{{ kolla_overcloud_inventory_path }}/"
+  when: kolla_ansible_custom_overcloud_group_vars.stat.exists
+
 - name: Ensure the Kolla overcloud host vars files exist
   template:
     src: host-vars.j2
diff --git a/ansible/roles/kolla-ansible/tests/test-defaults.yml b/ansible/roles/kolla-ansible/tests/test-defaults.yml
index c3f2c5cac9c9982af89b5f50a41f7c945888df86..a438506eb06dc99ce64012b6ce64b692c54a3aad 100644
--- a/ansible/roles/kolla-ansible/tests/test-defaults.yml
+++ b/ansible/roles/kolla-ansible/tests/test-defaults.yml
@@ -19,6 +19,9 @@
             kolla_ansible_venv: "{{ temp_path }}/venv"
             kolla_config_path: "{{ temp_path }}/etc/kolla"
             kolla_node_custom_config_path: "{{ temp_path }}/etc/kolla/config"
+            # Purposely does not exist to simulate the case when no group vars
+            # are provided
+            kolla_overcloud_group_vars_path: "{{ temp_path }}/etc/kayobe/kolla/inventory/group_vars"
             kolla_ansible_passwords_path: "{{ temp_path }}/passwords.yml"
             # Required config.
             kolla_base_distro: "fake-distro"
@@ -131,6 +134,7 @@
             - seed/host_vars
             - overcloud
             - overcloud/host_vars
+            - overcloud/group_vars
           register: inventory_stat
 
         - name: Validate inventory files
@@ -142,6 +146,18 @@
               Inventory file {{ item.item }} was not found.
           with_items: "{{ inventory_stat.results }}"
 
+        - name: Look for custom overcloud group vars
+          find:
+            paths: "{{ temp_path ~ '/etc/kolla/inventory/group_vars' }}"
+          register: kolla_ansible_overcloud_group_vars
+
+        - name: Check that no overcloud group vars are set
+          assert:
+            that:
+              - kolla_ansible_overcloud_group_vars.matched == 0
+            msg: >
+              Overcloud group vars were found when they should not be set.
+
         - name: Validate passwords.yml contents
           assert:
             that: item in passwords_yml
diff --git a/ansible/roles/kolla-ansible/tests/test-extras.yml b/ansible/roles/kolla-ansible/tests/test-extras.yml
index 9ee12d084903b4568b6393132271c7159be4d67b..f3671e0d332258d6e569e150a1bf06238ddd3f18 100644
--- a/ansible/roles/kolla-ansible/tests/test-extras.yml
+++ b/ansible/roles/kolla-ansible/tests/test-extras.yml
@@ -49,6 +49,25 @@
         state: directory
       register: tempfile_result
 
+    - name: Create directory for custom overcloud foo group vars
+      file:
+        path: "{{ tempfile_result.path ~ '/etc/kayobe/kolla/inventory/group_vars/foo_group' }}"
+        state: directory
+
+    - name: Create custom overcloud foo group vars
+      copy:
+        dest: "{{ tempfile_result.path ~ '/etc/kayobe/kolla/inventory/group_vars/foo_group/all' }}"
+        content: |
+          ---
+          foo_port: "1234"
+
+    - name: Create custom overcloud bar group vars
+      copy:
+        dest: "{{ tempfile_result.path ~ '/etc/kayobe/kolla/inventory/group_vars/bar_group' }}"
+        content: |
+          ---
+          bar_port: "4567"
+
     - block:
         - name: Test the kolla-ansible role with default values
           include_role:
@@ -61,6 +80,7 @@
             kolla_ansible_venv: "{{ temp_path }}/venv"
             kolla_ansible_vault_password: "fake-password"
             kolla_config_path: "{{ temp_path }}/etc/kolla"
+            kolla_overcloud_group_vars_path: "{{ temp_path }}/etc/kayobe/kolla/inventory/group_vars"
             kolla_node_custom_config_path: "{{ temp_path }}/etc/kolla/config"
             kolla_ansible_passwords_path: "{{ temp_path }}/passwords.yml"
             # Config.
@@ -496,6 +516,46 @@
                 neutron_external_interface: "eth4,eth5"
                 neutron_bridge_name: "br0,br1"
 
+        - name: Check whether inventory group vars files exist
+          stat:
+            path: "{{ temp_path ~ '/etc/kolla/inventory/overcloud/group_vars/' ~ item }}"
+          with_items:
+            - foo_group/all
+            - bar_group
+          register: group_vars_stat
+
+        - name: Validate inventory group vars files
+          assert:
+            that:
+              - item.stat.exists
+              - item.stat.size > 0
+            msg: >
+              Inventory file {{ item.item }} was not found.
+          with_items: "{{ group_vars_stat.results }}"
+
+        - name: Read inventory group vars files
+          slurp:
+            src: "{{ item.stat.path }}"
+          with_items: "{{ group_vars_stat.results }}"
+          register: group_vars_slurp
+
+        - name: Validate inventory group vars file contents
+          assert:
+            that:
+              - group_vars_content is defined
+              - group_vars_content == item.1
+          with_together:
+            - "{{ group_vars_slurp.results }}"
+            - "{{ expected_contents }}"
+          vars:
+            group_vars_content: "{{ item.0.content | b64decode }}"
+            expected_contents:
+              - |
+                ---
+                foo_port: "1234"
+              - |
+                ---
+                bar_port: "4567"
       always:
         - name: Ensure the temporary directory is removed
           file:
diff --git a/doc/source/configuration/kolla-ansible.rst b/doc/source/configuration/kolla-ansible.rst
index a29073409eb9445936ef7eead2cb1c8b32b64e32..c413243a26f0f6be9c2fff5537d49307a97c6492 100644
--- a/doc/source/configuration/kolla-ansible.rst
+++ b/doc/source/configuration/kolla-ansible.rst
@@ -299,6 +299,34 @@ to enable debug logging for Nova services:
    ---
    nova_logging_debug: true
 
+Custom Group Variables
+----------------------
+
+Group variables can be used to set configuration for all hosts in a group. They
+can be set in Kolla Ansible by placing files in
+``${KAYOBE_CONFIG_PATH}/kolla/inventory/group_vars/*``. Since this
+directory is copied directly into the Kolla Ansible inventory, Kolla
+Ansible group names should be used. It should be noted that
+``extra-vars`` and ``host_vars`` take precedence over ``group_vars``. For
+more information on variable precedence see the Ansible `documentation
+<http://docs.ansible.com/ansible/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable>`_.
+
+Example: configure a Nova cell
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In Kolla Ansible, :kolla-ansible-doc:`Nova cells are configured
+<reference/compute/nova-cells-guide>` via group variables. For example, to
+configure ``cell0001`` the following file could be created:
+
+.. code-block:: yaml
+   :caption: ``$KAYOBE_CONFIG_PATH/kolla/inventory/group_vars/cell0001/all``
+
+   ---
+   nova_cell_name: cell0001
+   nova_cell_novncproxy_group: cell0001-vnc
+   nova_cell_conductor_group: cell0001-control
+   nova_cell_compute_group: cell0001-compute
+
 Passwords
 ---------
 
diff --git a/releasenotes/notes/add-support-for-custom-group-vars-3760547c3505487d.yaml b/releasenotes/notes/add-support-for-custom-group-vars-3760547c3505487d.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..6633f7fba75163f975e42acdf5f4d3aabf55d604
--- /dev/null
+++ b/releasenotes/notes/add-support-for-custom-group-vars-3760547c3505487d.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - |
+    Adds support for passing through group vars to Kolla Ansible.