diff --git a/ansible/group_vars/all/dnf b/ansible/group_vars/all/dnf
index cea135a145946c20dd79488366c7a5f7e47ae0c7..64a7c249eb2584033e08277875f1cc67121098e3 100644
--- a/ansible/group_vars/all/dnf
+++ b/ansible/group_vars/all/dnf
@@ -7,12 +7,18 @@ dnf_config: {}
 # Whether or not to use a local Yum mirror. Default value is 'false'.
 dnf_use_local_mirror: false
 
-# Mirror FQDN for Yum repos. Default value is 'mirror.centos.org'.
+# Mirror FQDN for Yum CentOS repos. Default value is 'mirror.centos.org'.
 dnf_centos_mirror_host: 'mirror.centos.org'
 
 # Mirror directory for Yum CentOS repos. Default value is 'centos'.
 dnf_centos_mirror_directory: 'centos'
 
+# Mirror FQDN for Yum Rocky repos. Default value is 'dl.rockylinux.org'.
+dnf_rocky_mirror_host: 'dl.rockylinux.org'
+
+# Mirror directory for Yum Rocky repos. Default value is 'pub/rocky'.
+dnf_rocky_mirror_directory: 'pub/rocky'
+
 # Mirror FQDN for Yum EPEL repos. Default value is
 # 'download.fedoraproject.org'.
 dnf_epel_mirror_host: 'download.fedoraproject.org'
diff --git a/ansible/group_vars/all/globals b/ansible/group_vars/all/globals
index 3a7f362ce81e92b9563479e3d6bd5f3f54a39802..f84c60c34c7dd29024607471d8a0a57d031656ce 100644
--- a/ansible/group_vars/all/globals
+++ b/ansible/group_vars/all/globals
@@ -44,13 +44,17 @@ kayobe_ansible_user: "stack"
 ###############################################################################
 # OS distribution.
 
-# OS distribution name. Valid options are "centos", "ubuntu". Default is
-# "centos".
+# OS distribution name. Valid options are "centos", "rocky", "ubuntu". Default
+# is "centos".
 os_distribution: "centos"
 
 # OS release. Valid options are "8-stream" when os_distribution is "centos", or
-# "focal" when os_distribution is "ubuntu".
-os_release: "{{ '8-stream' if os_distribution == 'centos' else 'focal' }}"
+# "8" when os_distribution is "rocky", or "focal" when os_distribution is
+# "ubuntu".
+os_release: >-
+  {{ '8-stream' if os_distribution == 'centos'
+  else '8' if os_distribution == 'rocky'
+  else 'focal' }}
 
 ###############################################################################
 # Ansible configuration.
diff --git a/ansible/group_vars/all/infra-vms b/ansible/group_vars/all/infra-vms
index dbe4bfb83738a7721a691a5d9cc0603bceaf35cb..859eb046eb1a1b2a3660d2973ba77a2b360243ff 100644
--- a/ansible/group_vars/all/infra-vms
+++ b/ansible/group_vars/all/infra-vms
@@ -43,11 +43,16 @@ infra_vm_root_format: qcow2
 # Base image for the infra VM root volume. Default is
 # "https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img"
 # when os_distribution is "ubuntu", or
+# http://dl.rockylinux.org/pub/rocky/8.5/images/Rocky-8-GenericCloud-8.5-20211114.2.x86_64.qcow2
+# when os_distribution is "rocky",
+# or
 # "https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-20210603.0.x86_64.qcow2"
 # otherwise.
 infra_vm_root_image: >-
   {%- if os_distribution == 'ubuntu' %}
   https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
+  {%- elif os_distribution == 'rocky' %}
+  http://dl.rockylinux.org/pub/rocky/8.5/images/Rocky-8-GenericCloud-8.5-20211114.2.x86_64.qcow2
   {%- else -%}
   https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-20210603.0.x86_64.qcow2
   {%- endif %}
diff --git a/ansible/group_vars/all/kolla b/ansible/group_vars/all/kolla
index 883b08fab6aabac86c2ea7bec31dd8e91338256d..f10fd789c5a62dbb370a452ff05e49a6e70cc52b 100644
--- a/ansible/group_vars/all/kolla
+++ b/ansible/group_vars/all/kolla
@@ -53,8 +53,9 @@ kolla_node_custom_config_path: "{{ kolla_config_path }}/config"
 # Kolla configuration.
 
 # Kolla base container image distribution. Options are "centos", "debian",
-# "ubuntu". Default is {{ os_distribution }}.
-kolla_base_distro: "{{ os_distribution }}"
+# "ubuntu". Default is
+# {{ 'centos' if os_distribution == 'rocky' else os_distribution }}.
+kolla_base_distro: "{{ 'centos' if os_distribution == 'rocky' else os_distribution }}"
 
 # Kolla container image type: binary or source.
 kolla_install_type: "source"
diff --git a/ansible/group_vars/all/overcloud-dib b/ansible/group_vars/all/overcloud-dib
index c7bc700dacb7806eb0430562e19f3adfd3dce409..737d91832bac510277bba31cbdcdea8818110f12 100644
--- a/ansible/group_vars/all/overcloud-dib
+++ b/ansible/group_vars/all/overcloud-dib
@@ -7,8 +7,8 @@
 # Whether to build host disk images with DIB directly instead of through
 # Bifrost. Setting it to true disables Bifrost image build and allows images to
 # be built with the `kayobe overcloud host image build` command. Default value
-# is False. This will change in a future release.
-overcloud_dib_build_host_images: False
+# is {{ os_distribution == 'rocky' }}. This will change in a future release.
+overcloud_dib_build_host_images: "{{ os_distribution == 'rocky' }}"
 
 # List of overcloud host disk images to build. Each element is a dict defining
 # an image in a format accepted by the stackhpc.os-images role. Default is to
@@ -22,20 +22,24 @@ overcloud_dib_host_images:
     env: "{{ overcloud_dib_env_vars }}"
     packages: "{{ overcloud_dib_packages }}"
 
-# DIB base OS element. Default is {{ os_distribution }}.
-overcloud_dib_os_element: "{{ os_distribution }}"
+# DIB base OS element. Default is {{ 'rocky-container' if os_distribution ==
+# 'rocky' else os_distribution }}.
+overcloud_dib_os_element: "{{ 'rocky-container' if os_distribution == 'rocky' else os_distribution }}"
 
 # DIB image OS release. Default is {{ os_release }}.
 overcloud_dib_os_release: "{{ os_release }}"
 
 # List of default DIB elements. Default is ["centos", "cloud-init-datasources",
 # "disable-selinux", "enable-serial-console", "vm"] when
-# overcloud_dib_os_element is "centos", or ["ubuntu", "cloud-init-datasources",
-# "enable-serial-console", "vm"] when overcloud_dib_os_element is "ubuntu".
+# overcloud_dib_os_element is "centos", or ["rocky-container",
+# "cloud-init-datasources", "disable-selinux", "enable-serial-console", "vm"]
+# when overcloud_dib_os_element is "rocky" or
+# ["ubuntu", "cloud-init-datasources", "enable-serial-console", "vm"]
+# when overcloud_dib_os_element is "ubuntu".
 overcloud_dib_elements_default:
   - "{{ overcloud_dib_os_element }}"
   - "cloud-init-datasources"
-  - "{% if overcloud_dib_os_element == 'centos' %}disable-selinux{% endif %}"
+  - "{% if overcloud_dib_os_element in ['centos', 'rocky'] %}disable-selinux{% endif %}"
   - "enable-serial-console"
   - "vm"
 
@@ -48,11 +52,14 @@ overcloud_dib_elements: "{{ overcloud_dib_elements_default | select | list + ove
 
 # DIB default environment variables. Default is
 # {"DIB_BOOTLOADER_DEFAULT_CMDLINE": "nofb nomodeset gfxpayload=text
-# net.ifnames=1", "DIB_CLOUD_INIT_DATASOURCES": "ConfigDrive", "DIB_RELEASE":
-# "{{ overcloud_dib_os_release }}"}.
+# net.ifnames=1", "DIB_CLOUD_INIT_DATASOURCES": "ConfigDrive",
+# "DIB_CONTAINERFILE_RUNTIME": "docker", "DIB_CONTAINERFILE_NETWORK_DRIVER":
+# "host", "DIB_RELEASE": "{{ overcloud_dib_os_release }}"}.
 overcloud_dib_env_vars_default:
   DIB_BOOTLOADER_DEFAULT_CMDLINE: "nofb nomodeset gfxpayload=text net.ifnames=1"
   DIB_CLOUD_INIT_DATASOURCES: "ConfigDrive"
+  DIB_CONTAINERFILE_RUNTIME: "docker"
+  DIB_CONTAINERFILE_NETWORK_DRIVER: "host"
   DIB_RELEASE: "{{ overcloud_dib_os_release }}"
 
 # DIB additional environment variables. Default is none.
diff --git a/ansible/group_vars/all/seed-vm b/ansible/group_vars/all/seed-vm
index 1fe4d122f9fddee6e1a903128c884e3d2e4c9461..fb86584053969eb1ec46b23db66b1bdbf2f6e99a 100644
--- a/ansible/group_vars/all/seed-vm
+++ b/ansible/group_vars/all/seed-vm
@@ -42,12 +42,17 @@ seed_vm_root_format: qcow2
 
 # Base image for the seed VM root volume. Default is
 # "https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img"
-# when os_distribution is "ubuntu", or
+# when os_distribution is "ubuntu",
+# http://dl.rockylinux.org/pub/rocky/8.5/images/Rocky-8-GenericCloud-8.5-20211114.2.x86_64.qcow2
+# when os_distribution is "rocky",
+# or
 # "https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-20210603.0.x86_64.qcow2"
 # otherwise.
 seed_vm_root_image: >-
   {%- if os_distribution == 'ubuntu' %}
   https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
+  {%- elif os_distribution == 'rocky' %}
+  http://dl.rockylinux.org/pub/rocky/8.5/images/Rocky-8-GenericCloud-8.5-20211114.2.x86_64.qcow2
   {%- else -%}
   https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-20210603.0.x86_64.qcow2
   {%- endif %}
diff --git a/ansible/roles/dnf/tasks/local-mirror.yml b/ansible/roles/dnf/tasks/local-mirror.yml
index 845d50e96de68176bd67daa263211a8bd9be0ea3..7dc7b689978c690fa42f43cc2914fbf59ca59139 100644
--- a/ansible/roles/dnf/tasks/local-mirror.yml
+++ b/ansible/roles/dnf/tasks/local-mirror.yml
@@ -1,5 +1,7 @@
 ---
 - name: Copy CentOS repo templates
+  vars:
+    repo_file_prefix: "{{ 'CentOS-Stream' if ansible_facts.distribution == 'CentOS' else 'Rocky' }}"
   template:
     src: "{{ item }}.j2"
     dest: /etc/yum.repos.d/{{ item }}
@@ -8,9 +10,9 @@
     mode: 0664
   become: True
   loop:
-    - CentOS-Stream-AppStream.repo
-    - CentOS-Stream-BaseOS.repo
-    - CentOS-Stream-Extras.repo
+    - "{{ repo_file_prefix }}-AppStream.repo"
+    - "{{ repo_file_prefix }}-BaseOS.repo"
+    - "{{ repo_file_prefix }}-Extras.repo"
 
 - name: Remove old (pre CentOS 8.3) repo files
   file:
@@ -21,6 +23,7 @@
     - CentOS-AppStream.repo
     - CentOS-Base.repo
     - CentOS-Extras.repo
+  when: ansible_facts.distribution == 'CentOS'
 
 - name: Update cache
   dnf:
diff --git a/ansible/roles/dnf/templates/Rocky-AppStream.repo.j2 b/ansible/roles/dnf/templates/Rocky-AppStream.repo.j2
new file mode 100644
index 0000000000000000000000000000000000000000..e46b38deccffff8fb2f6a736cde9a4180ea6d1e1
--- /dev/null
+++ b/ansible/roles/dnf/templates/Rocky-AppStream.repo.j2
@@ -0,0 +1,16 @@
+# Rocky-AppStream.repo
+#
+# The mirrorlist system uses the connecting IP address of the client and the
+# update status of each mirror to pick current mirrors that are geographically
+# close to the client.  You should use this for Rocky updates unless you are
+# manually picking other mirrors.
+#
+# If the mirrorlist does not work for you, you can try the commented out
+# baseurl line instead.
+
+[appstream]
+name=Rocky Linux $releasever - AppStream
+baseurl=http://{{ dnf_rocky_mirror_host }}/{{ dnf_rocky_mirror_directory }}/$releasever/AppStream/$basearch/os/
+gpgcheck=1
+enabled=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial
diff --git a/ansible/roles/dnf/templates/Rocky-BaseOS.repo.j2 b/ansible/roles/dnf/templates/Rocky-BaseOS.repo.j2
new file mode 100644
index 0000000000000000000000000000000000000000..0931719bdc00212cdaa56fa3e5f780dacf1127ce
--- /dev/null
+++ b/ansible/roles/dnf/templates/Rocky-BaseOS.repo.j2
@@ -0,0 +1,16 @@
+# Rocky-BaseOS.repo
+#
+# The mirrorlist system uses the connecting IP address of the client and the
+# update status of each mirror to pick current mirrors that are geographically
+# close to the client.  You should use this for Rocky updates unless you are
+# manually picking other mirrors.
+#
+# If the mirrorlist does not work for you, you can try the commented out
+# baseurl line instead.
+
+[baseos]
+name=Rocky Linux $releasever - BaseOS
+baseurl=http://{{ dnf_rocky_mirror_host }}/{{ dnf_rocky_mirror_directory }}/$releasever/BaseOS/$basearch/os/
+gpgcheck=1
+enabled=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial
diff --git a/ansible/roles/dnf/templates/Rocky-Extras.repo.j2 b/ansible/roles/dnf/templates/Rocky-Extras.repo.j2
new file mode 100644
index 0000000000000000000000000000000000000000..ffb04c8f5ebfcb15645160d4a193dc07694b3e20
--- /dev/null
+++ b/ansible/roles/dnf/templates/Rocky-Extras.repo.j2
@@ -0,0 +1,16 @@
+# Rocky-Extras.repo
+#
+# The mirrorlist system uses the connecting IP address of the client and the
+# update status of each mirror to pick current mirrors that are geographically
+# close to the client.  You should use this for Rocky updates unless you are
+# manually picking other mirrors.
+#
+# If the mirrorlist does not work for you, you can try the commented out
+# baseurl line instead.
+
+[extras]
+name=Rocky Linux $releasever - Extras
+baseurl=http://{{ dnf_rocky_mirror_host }}/{{ dnf_rocky_mirror_directory }}/$releasever/extras/$basearch/os/
+gpgcheck=1
+enabled=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial
diff --git a/dev/functions b/dev/functions
index bd319be89adcedb4329c28dc4debed94f79903f8..3ece5f23fb6da06ad9f7a923d5d532e7e6966525 100644
--- a/dev/functions
+++ b/dev/functions
@@ -130,7 +130,7 @@ function config_init {
 # Installation
 
 function is_dnf {
-    if [[ -e /etc/centos-release ]]; then
+    if [[ -e /etc/centos-release || -e /etc/rocky-release ]]; then
         /usr/bin/which dnf >/dev/null 2>&1
     else
         return 1
@@ -138,7 +138,7 @@ function is_dnf {
 }
 
 function is_yum {
-    if [[ -e /etc/centos-release ]]; then
+    if [[ -e /etc/centos-release || -e /etc/rocky-release ]]; then
         /usr/bin/which yum >/dev/null 2>&1
     else
         return 1
diff --git a/doc/source/administration/infra-vms.rst b/doc/source/administration/infra-vms.rst
index 5633d1a00e537ac5dc46ef45dc04837069b944f7..71e9993acdfd34ff6b82a2be2220dd826d537a74 100644
--- a/doc/source/administration/infra-vms.rst
+++ b/doc/source/administration/infra-vms.rst
@@ -26,9 +26,9 @@ It is possible to update packages on the infrastructure VMs.
 Package Repositories
 --------------------
 
-If using custom DNF package repositories on CentOS, it may be necessary to
-update these prior to running a package update. To do this, update the
-configuration in ``${KAYOBE_CONFIG_PATH}/dnf.yml`` and run the following
+If using custom DNF package repositories on CentOS or Rocky, it may be
+necessary to update these prior to running a package update. To do this, update
+the configuration in ``${KAYOBE_CONFIG_PATH}/dnf.yml`` and run the following
 command::
 
     (kayobe) $ kayobe infra vm host configure --tags dnf
diff --git a/doc/source/administration/overcloud.rst b/doc/source/administration/overcloud.rst
index f4cfd2acdf30bc76158fbb8cd60b2c0bb543746e..6be18083a179c5f690e0954a5ce43475e7a0ab69 100644
--- a/doc/source/administration/overcloud.rst
+++ b/doc/source/administration/overcloud.rst
@@ -10,9 +10,9 @@ It is possible to update packages on the overcloud hosts.
 Package Repositories
 --------------------
 
-If using custom DNF package repositories on CentOS, it may be necessary to
-update these prior to running a package update. To do this, update the
-configuration in ``${KAYOBE_CONFIG_PATH}/dnf.yml`` and run the following
+If using custom DNF package repositories on CentOS or Rocky, it may be
+necessary to update these prior to running a package update. To do this, update
+the configuration in ``${KAYOBE_CONFIG_PATH}/dnf.yml`` and run the following
 command::
 
     (kayobe) $ kayobe overcloud host configure --tags dnf --kolla-tags none
diff --git a/doc/source/administration/seed.rst b/doc/source/administration/seed.rst
index c23428abd08ea7b16cd746eafe4d7e2178962a9b..d54499cd7df2a2363c5bed341885e0cb286ddfc2 100644
--- a/doc/source/administration/seed.rst
+++ b/doc/source/administration/seed.rst
@@ -21,9 +21,9 @@ It is possible to update packages on the seed host.
 Package Repositories
 --------------------
 
-If using custom DNF package repositories on CentOS, it may be necessary to
-update these prior to running a package update. To do this, update the
-configuration in ``${KAYOBE_CONFIG_PATH}/dnf.yml`` and run the following
+If using custom DNF package repositories on CentOS or Rocky, it may be
+necessary to update these prior to running a package update. To do this, update
+the configuration in ``${KAYOBE_CONFIG_PATH}/dnf.yml`` and run the following
 command::
 
     (kayobe) $ kayobe seed host configure --tags dnf --kolla-tags none
diff --git a/doc/source/configuration/reference/hosts.rst b/doc/source/configuration/reference/hosts.rst
index 66cd6d7908ff5a1268ce03ff4643e7ae48fbe33d..68ee55c1f8497cb7bc31f42dfbc3513f9b23c3fc 100644
--- a/doc/source/configuration/reference/hosts.rst
+++ b/doc/source/configuration/reference/hosts.rst
@@ -79,8 +79,8 @@ is ``stack``.
 Typically, the image used to provision these hosts will not include this user
 account, so Kayobe performs a bootstrapping step to create it, as a different
 user. In cloud images, there is often a user named after the OS distro, e.g.
-``centos`` or ``ubuntu``. This user defaults to the ``os_distribution``
-variable, but may be set via the following variables:
+``centos``, ``rocky`` or ``ubuntu``. This user defaults to the
+``os_distribution`` variable, but may be set via the following variables:
 
 * ``seed_hypervisor_bootstrap_user``
 * ``seed_bootstrap_user``
@@ -211,8 +211,8 @@ DNF Package Repositories
 *tags:*
   | ``dnf``
 
-On CentOS, Kayobe supports configuration of package repositories via DNF, via
-variables in ``${KAYOBE_CONFIG_PATH}/dnf.yml``.
+On CentOS and Rocky, Kayobe supports configuration of package repositories via
+DNF, via variables in ``${KAYOBE_CONFIG_PATH}/dnf.yml``.
 
 Configuration of dnf.conf
 -------------------------
@@ -227,18 +227,25 @@ section of the file. For example, to configure DNF to use a proxy server:
    dnf_config:
      proxy: https://proxy.example.com
 
-CentOS and EPEL Mirrors
------------------------
+CentOS/Rocky and EPEL Mirrors
+-----------------------------
 
-CentOS and EPEL mirrors can be enabled by setting ``dnf_use_local_mirror`` to
-``true``.  CentOS repository mirrors are configured via the following
-variables:
+CentOS/Rocky and EPEL mirrors can be enabled by setting
+``dnf_use_local_mirror`` to ``true``. CentOS repository mirrors are configured
+via the following variables:
 
 * ``dnf_centos_mirror_host`` (default ``mirror.centos.org``) is the mirror
   hostname.
 * ``dnf_centos_mirror_directory`` (default ``centos``) is a directory on the
   mirror in which repositories may be accessed.
 
+Rocky repository mirrors are configured via the following variables:
+
+* ``dnf_rocky_mirror_host`` (default ``dl.rockylinux.org``) is the mirror
+  hostname
+* ``dnf_rocky_mirror_directory`` (default ``pub/rocky``) is a directory on the
+  mirror in which repositories may be accessed.
+
 EPEL repository mirrors are configured via the following variables:
 
 * ``dnf_epel_mirror_host`` (default ``download.fedoraproject.org``) is the
@@ -327,7 +334,7 @@ SELinux
 *tags:*
   | ``disable-selinux``
 
-.. note:: SELinux applies to CentOS systems only.
+.. note:: SELinux applies to CentOS and Rocky systems only.
 
 SELinux is not supported by Kolla Ansible currently, so it is disabled by
 Kayobe. If necessary, Kayobe will reboot systems in order to apply a change to
@@ -348,12 +355,12 @@ Firewalld
 *tags:*
   | ``firewall``
 
-.. note:: Firewalld is supported on CentOS systems only. Currently no
+.. note:: Firewalld is supported on CentOS and Rocky systems only. Currently no
           firewall is supported on Ubuntu.
 
-Firewalld can be used to provide a firewall on CentOS systems. Since the Xena
-release, Kayobe provides support for enabling or disabling firewalld, as well
-as defining zones and rules.
+Firewalld can be used to provide a firewall on CentOS/Rocky systems. Since the
+Xena release, Kayobe provides support for enabling or disabling firewalld, as
+well as defining zones and rules.
 
 The following variables can be used to set whether to enable firewalld:
 
@@ -446,7 +453,7 @@ Tuned
 *tags:*
   | ``tuned``
 
-.. note:: Tuned configuration only supports CentOS systems for now.
+.. note:: Tuned configuration only supports CentOS/Rocky systems for now.
 
 Built-in ``tuned`` profiles can be applied to hosts. The following variables
 can be used to set a ``tuned`` profile to specific types of hosts:
diff --git a/doc/source/configuration/reference/network.rst b/doc/source/configuration/reference/network.rst
index e872e6a488e2cd21c8834dd4f623cd072d90d6db..01be73b35cd51b4be1f0860589a24c23ad641fd2 100644
--- a/doc/source/configuration/reference/network.rst
+++ b/doc/source/configuration/reference/network.rst
@@ -69,8 +69,8 @@ supported:
 ``rules``
     List of IP routing rules.
 
-    On CentOS, each item should be a string describing an ``iproute2`` IP
-    routing rule.
+    On CentOS or Rocky, each item should be a string describing an ``iproute2``
+    IP routing rule.
 
     On Ubuntu, each item should be a dict containing optional items ``from``,
     ``to``, ``priority`` and ``table``. ``from`` is the source address prefix
@@ -272,10 +272,10 @@ Configuring IP Routing Policy Rules
 
 IP routing policy rules may be configured by setting the ``rules`` attribute
 for a network to a list of rules. The format of each rule currently differs
-between CentOS and Ubuntu.
+between CentOS/Rocky and Ubuntu.
 
-CentOS
-""""""
+CentOS/Rocky
+""""""""""""
 
 The format of a rule is the string which would be appended to ``ip rule
 <add|del>`` to create or delete the rule.
diff --git a/doc/source/configuration/reference/os-distribution.rst b/doc/source/configuration/reference/os-distribution.rst
index 7347494cd7a09f0ad82b1512f458ef3913989a38..f73524f35c166f74dc3c55b78b5e98dba29901f8 100644
--- a/doc/source/configuration/reference/os-distribution.rst
+++ b/doc/source/configuration/reference/os-distribution.rst
@@ -11,12 +11,14 @@ used throughout the system.
 
 The ``os_distribution`` variable in ``etc/kayobe/globals.yml`` can be used to
 set the OS distribution to use.  It may be set to either ``centos`` or
-``ubuntu``, and defaults to ``centos``.
+or ``rocky`` or ``ubuntu``, and defaults to ``centos``.
 
 The ``os_release`` variable in ``etc/kayobe/globals.yml`` can be used to set
 the release of the OS. When ``os_distribution`` is set to ``centos`` it may be
 set to ``8-stream``, and this is its default value. When ``os_distribution`` is
 set to ``ubuntu`` it may be set to ``focal``, and this is its default value.
+When ``os_distribution`` is set to ``rocky`` it may be set to ``8``, and this
+is its default value.
 
 These variables are used to set various defaults, including:
 
@@ -34,3 +36,13 @@ In the following example, we set the OS distribution to ``ubuntu``:
    :caption: ``globals.yml``
 
    os_distribution: "ubuntu"
+
+Example: using Rocky
+====================
+
+In the following example, we set the OS distribution to ``rocky``:
+
+.. code-block:: yaml
+   :caption: ``globals.yml``
+
+   os_distribution: "rocky"
diff --git a/doc/source/configuration/reference/overcloud-dib.rst b/doc/source/configuration/reference/overcloud-dib.rst
index 57bd9eb782dd5c3b767fc3cc6411aa8a5dd0f733..86b8c97e58f11d787be3987bb90b2dd737eadb23 100644
--- a/doc/source/configuration/reference/overcloud-dib.rst
+++ b/doc/source/configuration/reference/overcloud-dib.rst
@@ -19,7 +19,8 @@ following option:
     Whether to build host disk images with DIB directly instead of through
     Bifrost. Setting it to true disables Bifrost image build and allows images
     to be built with the ``kayobe overcloud host image build`` command. Default
-    value is false. This will change in a future release.
+    value is false, except on Rocky where it is true. This will change in a
+    future release.
 
 With this option enabled, Bifrost will be configured to stop building a root
 disk image. This will become the default behaviour in a future release.
@@ -35,7 +36,8 @@ information on building disk images.
 
 The default configuration builds a whole disk (partitioned) image using the
 selected :ref:`OS distribution <os-distribution>` (CentOS Stream 8 by default)
-with serial console enabled, and SELinux disabled if CentOS Stream is used.
+with serial console enabled, and SELinux disabled if CentOS Stream or Rocky
+Linux is used.
 `Cloud-init <https://cloudinit.readthedocs.io/en/latest/>`__ is used to process
 the configuration drive built by Bifrost during provisioning.
 
@@ -48,17 +50,19 @@ the configuration drive built by Bifrost during provisioning.
     "elements": "{{ overcloud_dib_elements }}", "env": "{{
     overcloud_dib_env_vars }}", "packages": "{{ overcloud_dib_packages }}"}``.
 ``overcloud_dib_os_element``
-    DIB base OS element. Default is ``{{ os_distribution }}``.
+    DIB base OS element. Default is ``{{ 'rocky-container' if os_distribution == 'rocky' else os_distribution }}``.
 ``overcloud_dib_os_release``
     DIB image OS release. Default is ``{{ os_release }}``.
 ``overcloud_dib_elements_default``
     List of default DIB elements. Default is ``["centos",
     "cloud-init-datasources", "disable-selinux", "enable-serial-console",
-    "vm"]`` when ``overcloud_dib_os_element`` is ``centos``, or ``["ubuntu",
-    "cloud-init-datasources", "enable-serial-console", "vm"]`` when
-    ``overcloud_dib_os_element`` is ``ubuntu``. The ``vm`` element is poorly
-    named, and causes DIB to build a whole disk image rather than a single
-    partition.
+    "vm"]`` when ``overcloud_dib_os_element`` is ``centos``, or
+    ``["rocky-container", "cloud-init-datasources", "disable-selinux",
+    "enable-serial-console", "vm"]`` when overcloud_dib_os_element is ``rocky``
+    or ``["ubuntu", "cloud-init-datasources", "enable-serial-console", "vm"]``
+    when ``overcloud_dib_os_element`` is ``ubuntu``. The ``vm`` element is
+    poorly named, and causes DIB to build a whole disk image rather than a
+    single partition.
 ``overcloud_dib_elements_extra``
     List of additional DIB elements. Default is none.
 ``overcloud_dib_elements``
@@ -67,8 +71,9 @@ the configuration drive built by Bifrost during provisioning.
 ``overcloud_dib_env_vars_default``
     DIB default environment variables. Default is
     ``{"DIB_BOOTLOADER_DEFAULT_CMDLINE": "nofb nomodeset gfxpayload=text
-    net.ifnames=1", "DIB_CLOUD_INIT_DATASOURCES": "ConfigDrive", "DIB_RELEASE":
-    "{{ overcloud_dib_os_release }}"}``.
+    net.ifnames=1", "DIB_CLOUD_INIT_DATASOURCES": "ConfigDrive",
+    "DIB_CONTAINERFILE_RUNTIME": "docker", "DIB_CONTAINERFILE_NETWORK_DRIVER":
+    "host", DIB_RELEASE": "{{ overcloud_dib_os_release }}"}``.
 ``overcloud_dib_env_vars_extra``
     DIB additional environment variables. Default is none.
 ``overcloud_dib_env_vars``
diff --git a/doc/source/configuration/scenarios/all-in-one/index.rst b/doc/source/configuration/scenarios/all-in-one/index.rst
index ef66734672678522908d30fee66bf00bc683ed5a..750a2487dac3ce85ce8198f1e938b34b4e9148b8 100644
--- a/doc/source/configuration/scenarios/all-in-one/index.rst
+++ b/doc/source/configuration/scenarios/all-in-one/index.rst
@@ -30,9 +30,9 @@ It also requires a single host running a :ref:`supported operating system
 * at least one network interface that has Internet access
 
 You will need access to a user account with passwordless sudo. The default user
-in a cloud image (e.g. ``centos`` or ``ubuntu``) is typically sufficient. This
-user will be used to run Kayobe commands. It will also be used by Kayobe to
-bootstrap other user accounts.
+in a cloud image (e.g. ``centos`` or ``rocky`` or ``ubuntu``) is typically
+sufficient. This user will be used to run Kayobe commands. It will also be used
+by Kayobe to bootstrap other user accounts.
 
 .. _configuration-scenario-aio-overview:
 
diff --git a/doc/source/configuration/scenarios/all-in-one/overcloud.rst b/doc/source/configuration/scenarios/all-in-one/overcloud.rst
index e68938f16237b67eb2f02a125c01e794a31a1008..5e3b68ecaa45b81cf199dd7482b522bf05564b79 100644
--- a/doc/source/configuration/scenarios/all-in-one/overcloud.rst
+++ b/doc/source/configuration/scenarios/all-in-one/overcloud.rst
@@ -210,7 +210,8 @@ Use the correct hostname and IP address for your environment.
      controller0: 192.168.33.3
 
 The default OS distribution in Kayobe is CentOS. If using an Ubuntu host, set
-the ``os_distribution`` variable in ``etc/kayobe/globals.yml`` to ``ubuntu``.
+the ``os_distribution`` variable in ``etc/kayobe/globals.yml`` to ``ubuntu``
+or ``rocky`` if using Rocky Linux..
 
 .. code-block:: yaml
    :caption: ``etc/kayobe/globals.yml``
@@ -218,9 +219,9 @@ the ``os_distribution`` variable in ``etc/kayobe/globals.yml`` to ``ubuntu``.
    os_distribution: "ubuntu"
 
 Kayobe uses a bootstrap user to create a ``stack`` user account. By default,
-this user is ``centos`` on CentOS, and ``ubuntu`` on Ubuntu, in line with the
-default user in the official cloud images. If you are using a different
-bootstrap user, set the ``controller_bootstrap_user`` variable in
+this user is ``centos`` on CentOS, ``rocky`` on Rocky and ``ubuntu`` on Ubuntu,
+in line with the default user in the official cloud images. If you are using
+a different bootstrap user, set the ``controller_bootstrap_user`` variable in
 ``etc/kayobe/controllers.yml``. For example, to set it to ``cloud-user`` (as
 seen in MAAS):
 
diff --git a/doc/source/contributor/testing.rst b/doc/source/contributor/testing.rst
index 8c1e3d988ba96c9ffd7b45b4c6f9b15a716b8199..03d7d0454bd871b6565b19d4da2f0ade30521cf0 100644
--- a/doc/source/contributor/testing.rst
+++ b/doc/source/contributor/testing.rst
@@ -18,7 +18,7 @@ running kayobe's tests.
 
       sudo apt-get install build-essential python3-dev libssl-dev python3-pip git
 
-* Fedora or CentOS/RHEL 8::
+* Fedora or CentOS/Rocky 8/RHEL 8::
 
       sudo dnf install python3-devel openssl-devel python3-pip git gcc
 
diff --git a/doc/source/deployment.rst b/doc/source/deployment.rst
index 38b84549503a6da93972c4b57e2c56b26ec05622..44563759be64f1d51633dc8b80680ae471a5a3ac 100644
--- a/doc/source/deployment.rst
+++ b/doc/source/deployment.rst
@@ -102,10 +102,10 @@ VM Provisioning
    bare metal host or a VM provisioned outside of Kayobe, this step may be
    skipped.  Ensure that the Ansible inventory contains a host for the seed.
 
-The seed hypervisor should have CentOS or Ubuntu with ``libvirt`` installed.
-It should have ``libvirt`` networks configured for all networks that the seed
-VM needs access to and a ``libvirt`` storage pool available for the seed VM's
-volumes.  To provision the seed VM::
+The seed hypervisor should have CentOS or Rocky or Ubuntu with ``libvirt``
+installed. It should have ``libvirt`` networks configured for all networks
+that the seed VM needs access to and a ``libvirt`` storage pool available
+for the seed VM's volumes.  To provision the seed VM::
 
     (kayobe) $ kayobe seed vm provision
 
@@ -175,6 +175,11 @@ This command will also build the Operating System image that will be used to
 deploy the overcloud nodes using Disk Image Builder (DIB), unless
 ``overcloud_dib_build_host_images`` is set to ``True``.
 
+.. note::
+
+   If you are using Rocky Linux - building of the Operating System image
+   needs to be done using ``kayobe overcloud host image build``.
+
 To deploy the seed services in containers::
 
     (kayobe) $ kayobe seed service deploy
@@ -224,7 +229,8 @@ Building Overcloud Host Disk Images
 .. note::
 
    This step is only relevant if ``overcloud_dib_build_host_images`` is set to
-   ``True``. By default, a host disk image is automatically built by Bifrost.
+   ``True``. By default, a host disk image is automatically built by Bifrost
+   unless you're running Rocky Linux - which requires this step.
 
 Host disk images are deployed on overcloud hosts during provisioning. To build
 host disk images::
diff --git a/doc/source/installation.rst b/doc/source/installation.rst
index d4abf7aa92dfaf8eaa94abf74139335494936fd6..acf86eace98a422365e418d6b11d75ccddb5f79c 100644
--- a/doc/source/installation.rst
+++ b/doc/source/installation.rst
@@ -16,6 +16,7 @@ Currently Kayobe supports the following Operating Systems on the Ansible
 control host:
 
 - CentOS Stream 8 (since Wallaby 10.0.0 release)
+- Rocky Linux 8 (since Yoga 12.0.0 release)
 - Ubuntu Focal 20.04 (since Wallaby 10.0.0 release)
 
 See the :doc:`support matrix <support-matrix>` for details of supported
@@ -27,7 +28,7 @@ it is recommended to install Kayobe in a virtualenv. Ensure that the
 necessary to install the GCC compiler chain in order to build the extensions of
 some of kayobe's python dependencies.
 
-On CentOS::
+On CentOS/Rocky::
 
     $ dnf install -y python3-devel python3-virtualenv gcc libffi-devel
 
@@ -38,7 +39,7 @@ On Ubuntu::
 If installing Kayobe from source, then Git is required for cloning and working
 with the source code repository.
 
-On CentOS::
+On CentOS/Rocky::
 
     $ dnf install -y git
 
diff --git a/doc/source/support-matrix.rst b/doc/source/support-matrix.rst
index ac5d5a7c5acd308a44c26e4987ffd4adeedc77ec..3b18d665e78f9bd8145471118095a289a87a113a 100644
--- a/doc/source/support-matrix.rst
+++ b/doc/source/support-matrix.rst
@@ -10,6 +10,7 @@ Supported Operating Systems
 Kayobe supports the following host Operating Systems (OS):
 
 * CentOS Stream 8 (since Wallaby 10.0.0 release)
+* Rocky Linux 8 (since Yoga 12.0.0 release)
 * Ubuntu Focal 20.04 (since Wallaby 10.0.0 release)
 
 .. note::
diff --git a/etc/kayobe/dnf.yml b/etc/kayobe/dnf.yml
index 1af08f622fd4fb5ab55330b6ecd476664a66cb34..da4ea22eb35acc712b863f10ea1fa90e5a8b1146 100644
--- a/etc/kayobe/dnf.yml
+++ b/etc/kayobe/dnf.yml
@@ -12,12 +12,18 @@
 # Whether or not to use a local Yum mirror. Default value is 'false'.
 #dnf_use_local_mirror:
 
-# Mirror FQDN for Yum repos. Default value is 'mirror.centos.org'.
+# Mirror FQDN for Yum CentOS repos. Default value is 'mirror.centos.org'.
 #dnf_centos_mirror_host:
 
 # Mirror directory for Yum CentOS repos. Default value is 'centos'.
 #dnf_centos_mirror_directory:
 
+# Mirror FQDN for Yum Rocky repos. Default value is 'dl.rockylinux.org'.
+#dnf_rocky_mirror_host:
+
+# Mirror directory for Yum Rocky repos. Default value is 'pub/rocky'.
+#dnf_rocky_mirror_directory:
+
 # Mirror FQDN for Yum EPEL repos. Default value is
 # 'download.fedoraproject.org'.
 #dnf_epel_mirror_host:
diff --git a/etc/kayobe/globals.yml b/etc/kayobe/globals.yml
index a4150d8eca271073663745171e7323f6760e50c0..b926fc9be10b7ad8014fb58f813d1ce6199bf952 100644
--- a/etc/kayobe/globals.yml
+++ b/etc/kayobe/globals.yml
@@ -45,12 +45,13 @@
 ###############################################################################
 # OS distribution.
 
-# OS distribution name. Valid options are "centos", "ubuntu". Default is
-# "centos".
+# OS distribution name. Valid options are "centos", "rocky", "ubuntu". Default
+# is "centos".
 #os_distribution:
 
 # OS release. Valid options are "8-stream" when os_distribution is "centos", or
-# "focal" when os_distribution is "ubuntu".
+# "8" when os_distribution is "rocky", or "focal" when os_distribution is
+# "ubuntu".
 #os_release:
 
 ###############################################################################
diff --git a/etc/kayobe/infra-vms.yml b/etc/kayobe/infra-vms.yml
index dc7ff23363c10267e0bbb57549066caa4894a17e..3274ed806051bd17af48ac67761c3876898f6fdb 100644
--- a/etc/kayobe/infra-vms.yml
+++ b/etc/kayobe/infra-vms.yml
@@ -32,6 +32,9 @@
 # Base image for the infra VM root volume. Default is
 # "https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img"
 # when os_distribution is "ubuntu", or
+# http://dl.rockylinux.org/pub/rocky/8.5/images/Rocky-8-GenericCloud-8.5-20211114.2.x86_64.qcow2
+# when os_distribution is "rocky",
+# or
 # "https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-20210603.0.x86_64.qcow2"
 # otherwise.
 #infra_vm_root_image:
diff --git a/etc/kayobe/kolla.yml b/etc/kayobe/kolla.yml
index 3ed09d6c868e132b88ab01e18c431e1c5cb67ff4..01d2a8da9c24205eac1a9f2dd5a7ef611c58bc43 100644
--- a/etc/kayobe/kolla.yml
+++ b/etc/kayobe/kolla.yml
@@ -62,7 +62,8 @@
 # Kolla configuration.
 
 # Kolla base container image distribution. Options are "centos", "debian",
-# "ubuntu". Default is {{ os_distribution }}.
+# "ubuntu". Default is
+# {{ 'centos' if os_distribution == 'rocky' else os_distribution }}.
 #kolla_base_distro:
 
 # Kolla container image type: binary or source. Default is 'source'.
diff --git a/etc/kayobe/overcloud-dib.yml b/etc/kayobe/overcloud-dib.yml
index f53804e421999a8d4fba2b3f44a4a35ba0f9b79c..3d69f8eb1243040e0fbd6bb5248cc2b82b283b0c 100644
--- a/etc/kayobe/overcloud-dib.yml
+++ b/etc/kayobe/overcloud-dib.yml
@@ -7,7 +7,7 @@
 # Whether to build host disk images with DIB directly instead of through
 # Bifrost. Setting it to true disables Bifrost image build and allows images to
 # be built with the `kayobe overcloud host image build` command. Default value
-# is False. This will change in a future release.
+# is {{ os_distribution == 'rocky' }}. This will change in a future release.
 #overcloud_dib_build_host_images:
 
 # List of overcloud host disk images to build. Each element is a dict defining
@@ -18,7 +18,8 @@
 # "packages": "{{ overcloud_dib_packages }}"}.
 #overcloud_dib_host_images:
 
-# DIB base OS element. Default is {{ os_distribution }}.
+# DIB base OS element. Default is {{ 'rocky-container' if os_distribution ==
+# 'rocky' else os_distribution }}.
 #overcloud_dib_os_element:
 
 # DIB image OS release. Default is {{ os_release }}.
@@ -26,8 +27,11 @@
 
 # List of default DIB elements. Default is ["centos", "cloud-init-datasources",
 # "disable-selinux", "enable-serial-console", "vm"] when
-# overcloud_dib_os_element is "centos", or ["ubuntu", "cloud-init-datasources",
-# "enable-serial-console", "vm"] when overcloud_dib_os_element is "ubuntu".
+# overcloud_dib_os_element is "centos", or ["rocky-container",
+# "cloud-init-datasources", "disable-selinux", "enable-serial-console", "vm"]
+# when overcloud_dib_os_element is "rocky" or
+# ["ubuntu", "cloud-init-datasources", "enable-serial-console", "vm"]
+# when overcloud_dib_os_element is "ubuntu".
 #overcloud_dib_elements_default:
 
 # List of additional DIB elements. Default is none.
@@ -39,8 +43,9 @@
 
 # DIB default environment variables. Default is
 # {"DIB_BOOTLOADER_DEFAULT_CMDLINE": "nofb nomodeset gfxpayload=text
-# net.ifnames=1", "DIB_CLOUD_INIT_DATASOURCES": "ConfigDrive", "DIB_RELEASE":
-# "{{ overcloud_dib_os_release }}"}.
+# net.ifnames=1", "DIB_CLOUD_INIT_DATASOURCES": "ConfigDrive",
+# "DIB_CONTAINERFILE_RUNTIME": "docker", "DIB_CONTAINERFILE_NETWORK_DRIVER":
+# "host", "DIB_RELEASE": "{{ overcloud_dib_os_release }}"}.
 #overcloud_dib_env_vars_default:
 
 # DIB additional environment variables. Default is none.
diff --git a/etc/kayobe/seed-vm.yml b/etc/kayobe/seed-vm.yml
index 8e20fc7895059009df22339682f7ebda32f55e79..f8c2ab8a9c8d55749f7daae6383587b2dde485ea 100644
--- a/etc/kayobe/seed-vm.yml
+++ b/etc/kayobe/seed-vm.yml
@@ -25,7 +25,10 @@
 
 # Base image for the seed VM root volume. Default is
 # "https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img"
-# when os_distribution is "ubuntu", or
+# when os_distribution is "ubuntu",
+# http://dl.rockylinux.org/pub/rocky/8.5/images/Rocky-8-GenericCloud-8.5-20211114.2.x86_64.qcow2
+# when os_distribution is "rocky",
+# or
 # "https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-20210603.0.x86_64.qcow2"
 # otherwise.
 #seed_vm_root_image:
diff --git a/playbooks/kayobe-infra-vm-base/pre.yml b/playbooks/kayobe-infra-vm-base/pre.yml
index 9a520db8b5ac42ac319b7dba40d46037b137ad96..38c9a6e2af9e2bd74fce0cb25958ba8f3b8affc5 100644
--- a/playbooks/kayobe-infra-vm-base/pre.yml
+++ b/playbooks/kayobe-infra-vm-base/pre.yml
@@ -36,7 +36,7 @@
       selinux:
         state: disabled
       become: True
-      when: ansible_os_family == 'RedHat'
+      when: ansible_os_family in ['RedHat', 'Rocky']
 
     # NOTE(mgoddard): Use the name zz-overrides.yml to ensure this takes
     # precedence over the standard config files.
diff --git a/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2 b/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2
index 20f0c647220853933d66ddfc67abb00e6bce0b11..a6de4e74f71109f23685240e3dd5159a1c05db71 100644
--- a/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2
+++ b/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2
@@ -41,7 +41,7 @@ test_net_eth_vlan_routes:
     gateway: 192.168.35.254
     table: kayobe-test-route-table
 test_net_eth_vlan_rules:
-{% if ansible_os_family == 'RedHat' %}
+{% if ansible_os_family in ['RedHat', 'Rocky'] %}
   - from 192.168.35.0/24 table kayobe-test-route-table
 {% else %}
   - from: 192.168.35.0/24
@@ -114,13 +114,15 @@ docker_storage_driver: devicemapper
 # Set Honolulu time.
 timezone: Pacific/Honolulu
 
-{% if ansible_os_family == 'RedHat' %}
+{% if ansible_os_family in ['RedHat', 'Rocky'] %}
 # Use a local DNF mirror.
 dnf_use_local_mirror: true
+{% if ansible_distribution == 'CentOS' %}
 # Mirror FQDN for DNF repos.
 dnf_centos_mirror_host: "{{ zuul_site_mirror_fqdn }}"
 # Mirror directory for DNF CentOS repos.
 dnf_centos_mirror_directory: 'centos'
+{% endif %}
 # Mirror FQDN for DNF EPEL repos.
 dnf_epel_mirror_host: "{{ zuul_site_mirror_fqdn }}"
 # Mirror directory for DNF EPEL repos.
diff --git a/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py b/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py
index 041015e3515fbbdf3e8e08472726b9ec85f6110e..8b335c11b42e70aa072964b31107597bd69608a8 100644
--- a/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py
+++ b/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py
@@ -12,14 +12,17 @@ import pytest
 
 
 def _is_firewalld_supported():
-    info = distro.linux_distribution()
-    return info[0].startswith('CentOS')
+    info = distro.id()
+    return info in ['centos', 'rocky']
 
 
 def _is_dnf():
-    info = distro.linux_distribution()
-    return info[0].startswith('CentOS')
+    info = distro.id()
+    return info in ['centos', 'rocky']
 
+def _is_dnf_mirror():
+    info = distro.id()
+    return info == 'centos'
 
 def test_network_ethernet(host):
     interface = host.interface('dummy2')
@@ -168,7 +171,8 @@ def test_ntp_running(host):
 def test_ntp_non_default_time_server(host):
     # Tests that the NTP pool has been changed from pool.ntp.org to
     # time.cloudflare.com
-    if 'centos' in host.system_info.distribution.lower():
+    if ('centos' in host.system_info.distribution.lower() or 
+       'rocky' in host.system_info.distribution.lower()):
         chrony_config = host.file("/etc/chrony.conf")
     else:
         # Debian based distributions use the following path
@@ -185,7 +189,7 @@ def test_ntp_clock_synchronized(host):
 
 @pytest.mark.parametrize('repo', ["appstream", "baseos", "extras", "epel",
                                   "epel-modular"])
-@pytest.mark.skipif(not _is_dnf(), reason="DNF only supported on CentOS 8")
+@pytest.mark.skipif(not _is_dnf_mirror(), reason="DNF OpenDev mirror only for CentOS 8")
 def test_dnf_local_package_mirrors(host, repo):
     # Depends on SITE_MIRROR_FQDN environment variable.
     assert os.getenv('SITE_MIRROR_FQDN')
@@ -197,14 +201,14 @@ def test_dnf_local_package_mirrors(host, repo):
     assert os.getenv('SITE_MIRROR_FQDN') in info
 
 
-@pytest.mark.skipif(not _is_dnf(), reason="DNF only supported on CentOS 8")
+@pytest.mark.skipif(not _is_dnf(), reason="DNF only supported on CentOS 8/Rocky 8")
 def test_dnf_custom_package_repository_is_available(host):
     with host.sudo():
         host.check_output("dnf -y install td-agent")
     assert host.package("td-agent").is_installed
 
 
-@pytest.mark.skipif(not _is_dnf(), reason="DNF only supported on CentOS 8")
+@pytest.mark.skipif(not _is_dnf(), reason="DNF only supported on CentOS 8/Rocky 8")
 def test_dnf_automatic(host):
     assert host.package("dnf-automatic").is_installed
     assert host.service("dnf-automatic.timer").is_enabled
@@ -212,14 +216,14 @@ def test_dnf_automatic(host):
 
 
 @pytest.mark.skipif(not _is_dnf(),
-                    reason="tuned profile setting only supported on CentOS 8")
+                    reason="tuned profile setting only supported on CentOS 8/Rocky 8")
 def test_tuned_profile_is_active(host):
     tuned_output = host.check_output("tuned-adm active")
     assert "throughput-performance" in tuned_output
 
 
 @pytest.mark.skipif(not _is_firewalld_supported(),
-                    reason="Firewalld only supported on CentOS")
+                    reason="Firewalld only supported on CentOS and Rocky")
 def test_firewalld_running(host):
     assert host.package("firewalld").is_installed
     assert host.service("firewalld.service").is_enabled
@@ -227,7 +231,7 @@ def test_firewalld_running(host):
 
 
 @pytest.mark.skipif(not _is_firewalld_supported(),
-                    reason="Firewalld only supported on CentOS")
+                    reason="Firewalld only supported on CentOS and Rocky")
 def test_firewalld_zones(host):
     # Verify that interfaces are on correct zones.
     expected_zones = {
@@ -250,7 +254,7 @@ def test_firewalld_zones(host):
 
 
 @pytest.mark.skipif(not _is_firewalld_supported(),
-                    reason="Firewalld only supported on CentOS")
+                    reason="Firewalld only supported on CentOS and Rocky")
 def test_firewalld_rules(host):
     # Verify that expected rules are present.
     expected_info = {
diff --git a/playbooks/kayobe-seed-vm-base/overrides.yml.j2 b/playbooks/kayobe-seed-vm-base/overrides.yml.j2
index 55e5a8fd546f62bb3b5955340dd5a73aa7c4dc28..9c5462c73630fc7d5e6ac3cb037a0c149ce53820 100644
--- a/playbooks/kayobe-seed-vm-base/overrides.yml.j2
+++ b/playbooks/kayobe-seed-vm-base/overrides.yml.j2
@@ -50,3 +50,5 @@ configdrive_debian_network_interfaces_supports_glob: false
 # are using for SSH to be removed. Use a dummy interface.
 aio_bridge_ports:
   - dummy1
+
+overcloud_dib_build_host_images: False
diff --git a/playbooks/kayobe-seed-vm-base/pre.yml b/playbooks/kayobe-seed-vm-base/pre.yml
index 8a369a6b7a0c56d2bdf9dae11ab40df3aa8fc92d..0e82db2945519284a0be014a377685a4376136dc 100644
--- a/playbooks/kayobe-seed-vm-base/pre.yml
+++ b/playbooks/kayobe-seed-vm-base/pre.yml
@@ -36,7 +36,7 @@
       selinux:
         state: disabled
       become: True
-      when: ansible_os_family == 'RedHat'
+      when: ansible_os_family in ['RedHat', 'Rocky']
 
     # NOTE(mgoddard): Use the name zz-overrides.yml to ensure this takes
     # precedence over the standard config files.
diff --git a/releasenotes/notes/support-rockylinux-8-1da50e2f97b918d5.yaml b/releasenotes/notes/support-rockylinux-8-1da50e2f97b918d5.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..35ea220167b5d5dcca2b2711aac188f3d864e033
--- /dev/null
+++ b/releasenotes/notes/support-rockylinux-8-1da50e2f97b918d5.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - |
+    Adds support for Rocky Linux 8 as Host OS.
diff --git a/roles/kayobe-ci-prep/tasks/main.yml b/roles/kayobe-ci-prep/tasks/main.yml
index c90741b7afe2e64e8a5de7d233a15022f5d14759..d19bef33138a8d263e74ba479c8c1f7e43058586 100644
--- a/roles/kayobe-ci-prep/tasks/main.yml
+++ b/roles/kayobe-ci-prep/tasks/main.yml
@@ -17,5 +17,5 @@
 
     - name: Enable the EPEL repository
       command: dnf config-manager --disable epel
-  when: ansible_os_family == 'RedHat'
+  when: ansible_os_family in ['RedHat', 'Rocky']
   become: true
diff --git a/zuul.d/jobs.yaml b/zuul.d/jobs.yaml
index 761d5f966db9edab7535c660f18c1bdd894d34cd..c151218ddeb3461fa075e4ce898907a0fde5121d 100644
--- a/zuul.d/jobs.yaml
+++ b/zuul.d/jobs.yaml
@@ -107,6 +107,11 @@
     parent: kayobe-overcloud-base
     nodeset: kayobe-centos8s
 
+- job:
+    name: kayobe-overcloud-rocky8
+    parent: kayobe-overcloud-base
+    nodeset: kayobe-rocky8
+
 - job:
     name: kayobe-overcloud-ubuntu-focal
     parent: kayobe-overcloud-base
@@ -161,6 +166,11 @@
     parent: kayobe-seed-base
     nodeset: kayobe-centos8s
 
+- job:
+    name: kayobe-seed-rocky8
+    parent: kayobe-seed-base
+    nodeset: kayobe-rocky8
+
 - job:
     name: kayobe-seed-ubuntu-focal
     parent: kayobe-seed-base
@@ -182,6 +192,11 @@
     parent: kayobe-overcloud-host-configure-base
     nodeset: kayobe-centos8s
 
+- job:
+    name: kayobe-overcloud-host-configure-rocky8
+    parent: kayobe-overcloud-host-configure-base
+    nodeset: kayobe-rocky8
+
 - job:
     name: kayobe-overcloud-host-configure-ubuntu-focal
     parent: kayobe-overcloud-host-configure-base
@@ -226,6 +241,11 @@
     parent: kayobe-seed-vm-base
     nodeset: kayobe-centos8s
 
+- job:
+    name: kayobe-seed-vm-rocky8
+    parent: kayobe-seed-vm-base
+    nodeset: kayobe-rocky8
+
 - job:
     name: kayobe-seed-vm-ubuntu-focal
     parent: kayobe-seed-vm-base
@@ -248,6 +268,11 @@
     parent: kayobe-infra-vm-base
     nodeset: kayobe-centos8s
 
+- job:
+    name: kayobe-infra-vm-rocky8
+    parent: kayobe-infra-vm-base
+    nodeset: kayobe-rocky8
+
 - job:
     name: kayobe-infra-vm-ubuntu-focal
     parent: kayobe-infra-vm-base
diff --git a/zuul.d/nodesets.yaml b/zuul.d/nodesets.yaml
index f897323630fe2f31c6deaa3eb686b923d69a0506..b20e1bdeaa4f78076913f8ca5e0a805e5ba9e30a 100644
--- a/zuul.d/nodesets.yaml
+++ b/zuul.d/nodesets.yaml
@@ -5,6 +5,13 @@
       - name: primary
         label: centos-8-stream
 
+
+- nodeset:
+    name: kayobe-rocky8
+    nodes:
+      - name: primary
+        label: rockylinux-8
+
 - nodeset:
     name: kayobe-ubuntu-focal
     nodes:
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index 01b6d4aadabdf20232e64fc14432faf2c3991b27..02311ad642a1eda6525d0a066faadddcabe97025 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -11,19 +11,24 @@
         - kayobe-tox-ansible
         - kayobe-tox-molecule
         - kayobe-overcloud-centos8s
+        - kayobe-overcloud-rocky8
         - kayobe-overcloud-ubuntu-focal
         - kayobe-overcloud-tls-centos8s
         - kayobe-overcloud-host-configure-centos8s
+        - kayobe-overcloud-host-configure-rocky8
         - kayobe-overcloud-host-configure-ubuntu-focal
         - kayobe-overcloud-upgrade-centos8s
         - kayobe-overcloud-upgrade-ubuntu-focal
         - kayobe-seed-centos8s
+        - kayobe-seed-rocky8
         - kayobe-seed-ubuntu-focal
         - kayobe-seed-upgrade-centos8s
         - kayobe-seed-upgrade-ubuntu-focal
         - kayobe-seed-vm-centos8s
+        - kayobe-seed-vm-rocky8
         - kayobe-seed-vm-ubuntu-focal
         - kayobe-infra-vm-centos8s
+        - kayobe-infra-vm-rocky8
         - kayobe-infra-vm-ubuntu-focal
 
     gate:
@@ -33,17 +38,22 @@
         - kayobe-tox-ansible
         - kayobe-tox-molecule
         - kayobe-overcloud-centos8s
+        - kayobe-overcloud-rocky8
         - kayobe-overcloud-ubuntu-focal
         - kayobe-overcloud-tls-centos8s
         - kayobe-overcloud-host-configure-centos8s
+        - kayobe-overcloud-host-configure-rocky8
         - kayobe-overcloud-host-configure-ubuntu-focal
         - kayobe-overcloud-upgrade-centos8s
         - kayobe-overcloud-upgrade-ubuntu-focal
         - kayobe-seed-centos8s
+        - kayobe-seed-rocky8
         - kayobe-seed-ubuntu-focal
         - kayobe-seed-upgrade-centos8s
         - kayobe-seed-upgrade-ubuntu-focal
         - kayobe-seed-vm-centos8s
+        - kayobe-seed-vm-rocky8
         - kayobe-seed-vm-ubuntu-focal
         - kayobe-infra-vm-centos8s
+        - kayobe-infra-vm-rocky8
         - kayobe-infra-vm-ubuntu-focal