diff --git a/ansible/group_vars/all/overcloud-dib b/ansible/group_vars/all/overcloud-dib
index 9c0c0ffdc191ea013c491102c3332b2fe174c276..1cc77fa3f217effec6a36822613f554b61d49edb 100644
--- a/ansible/group_vars/all/overcloud-dib
+++ b/ansible/group_vars/all/overcloud-dib
@@ -10,6 +10,18 @@
 # is False. This will change in a future release.
 overcloud_dib_build_host_images: False
 
+# 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
+# build an image named "deployment_image" configured with the overcloud_dib_*
+# variables defined below: {"name": "deployment_image", "elements": "{{
+# overcloud_dib_elements }}", "env": "{{ overcloud_dib_env_vars }}",
+# "packages": "{{ overcloud_dib_packages }}"}.
+overcloud_dib_host_images:
+  - name: "deployment_image"
+    elements: "{{ overcloud_dib_elements }}"
+    env: "{{ overcloud_dib_env_vars }}"
+    packages: "{{ overcloud_dib_packages }}"
+
 # DIB base OS element. Default is {{ os_distribution }}.
 overcloud_dib_os_element: "{{ os_distribution }}"
 
diff --git a/ansible/overcloud-host-image-build.yml b/ansible/overcloud-host-image-build.yml
index 82d4b305556bb9d92e902791105610565553f874..4751729b1bed9a3d2094c536a9f72d770b7b1ac5 100644
--- a/ansible/overcloud-host-image-build.yml
+++ b/ansible/overcloud-host-image-build.yml
@@ -1,19 +1,24 @@
 ---
-# Build and install a overcloud host disk image for the seed host's ironic
+# Build and install overcloud host disk images for the seed host's ironic
 # service.
 
-- name: Ensure overcloud host disk image is built and installed
+- name: Ensure overcloud host disk images are built and installed
   hosts: seed
   tags:
     - overcloud-host-image-build
   vars:
-    overcloud_host_image_name: "deployment_image"
-    overcloud_host_disk_images:
-      - "{{ overcloud_host_image_name }}.qcow2"
     overcloud_host_image_force_rebuild: False
   tasks:
     - block:
-        - name: Ensure overcloud host disk image is built
+        - name: Validate overcloud host disk image configuration
+          assert:
+            that:
+              - overcloud_dib_host_images is sequence
+              - overcloud_dib_host_images | selectattr('name', 'undefined') | list | length == 0
+              - overcloud_dib_host_images | selectattr('elements', 'undefined') | list | length == 0
+            msg: "overcloud_dib_host_images set to invalid value"
+
+        - name: Ensure overcloud host disk images are built
           include_role:
             name: stackhpc.os-images
           vars:
@@ -22,30 +27,17 @@
             os_images_upper_constraints_file: "{{ overcloud_dib_upper_constraints_file }}"
             os_images_cache: "{{ image_cache_path }}"
             os_images_common: ""
-            os_images_list:
-              - name: "{{ overcloud_host_image_name }}"
-                elements: "{{ overcloud_dib_elements }}"
-                env: "{{ overcloud_dib_env_vars }}"
-                packages: "{{ overcloud_dib_packages }}"
-                type: qcow2
+            os_images_list: "{{ overcloud_dib_host_images }}"
             os_images_upload: False
             os_images_force_rebuild: "{{ overcloud_host_image_force_rebuild }}"
 
-        - name: Ensure overcloud host disk image is copied onto seed
+        - name: Copy overcloud host disk images into /httpboot
           copy:
-            src: "{{ image_cache_path }}/{{ overcloud_host_image_name }}/{{ item }}"
-            dest: "/etc/kolla/bifrost/{{ item }}"
+            src: "{{ image_cache_path }}/{{ image.name }}/{{ image.name }}.{{ image.type | default('qcow2') }}"
+            dest: "/var/lib/docker/volumes/bifrost_httpboot/_data/{{ image.name }}.{{ image.type | default('qcow2') }}"
             remote_src: True
-          with_items: "{{ overcloud_host_disk_images }}"
+          with_items: "{{ overcloud_dib_host_images }}"
+          loop_control:
+            loop_var: image
           become: True
-
-        - name: Copy overcloud host disk image into /httpboot
-          command: >
-            docker exec bifrost_deploy
-            bash -c 'ansible -vvvv target
-            -i /bifrost/playbooks/inventory/target
-            -m copy
-            -a "src=/etc/bifrost/{{ item }} dest=/httpboot/{{ item }}"
-            -e "ansible_python_interpreter=/var/lib/kolla/venv/bin/python"'
-          with_items: "{{ overcloud_host_disk_images }}"
       when: overcloud_dib_build_host_images | bool
diff --git a/doc/source/configuration/reference/bifrost.rst b/doc/source/configuration/reference/bifrost.rst
index 7816cffc7c22f32580c93f652e60b150a9b2df8b..8e5667dfd72eedca5a850d69fc5df4f17abf94e1 100644
--- a/doc/source/configuration/reference/bifrost.rst
+++ b/doc/source/configuration/reference/bifrost.rst
@@ -39,6 +39,11 @@ For example, to install Bifrost from a custom git repository:
 Overcloud root disk image configuration
 =======================================
 
+.. note::
+
+   This configuration only applies when ``overcloud_dib_build_host_images``
+   (set in ``${KAYOBE_CONFIG_PATH}/overcloud-dib.yml``) is not changed to true.
+
 Bifrost uses Diskimage builder (DIB) to build a root disk image that is
 deployed to overcloud hosts when they are provisioned. The following options
 configure how this image is built.  Consult the
@@ -180,6 +185,8 @@ Rather than needing to write a custom DIB element, we can use the
    kolla_bifrost_dib_packages:
      - "biosdevname"
 
+.. _configuration-bifrost-image-deployment-config:
+
 Disk image deployment configuration
 ===================================
 
@@ -190,11 +197,10 @@ The name of the root disk image to deploy can be configured via the
 Kayobe inventory. This can be used to provision different images across the
 overcloud.
 
-.. note::
-
-   Support for building multiple disk images is not yet available. Images can
-   be manually renamed before changing the Kayobe configuration to build a
-   different image.
+While only a single disk image can be built with Bifrost, starting from the
+Yoga 12.0.0 release, Kayobe supports building multiple disk images directly
+through Diskimage builder. Consult the :ref:`overcloud host disk image build
+documentation <overcloud-dib>` for more details.
 
 Ironic configuration
 ====================
diff --git a/doc/source/configuration/reference/index.rst b/doc/source/configuration/reference/index.rst
index 2085025a1df620a25bb2f99eaa331c9e69d3ed3a..100b7ae8480c8d3935a810892e7f1dbe53647dae 100644
--- a/doc/source/configuration/reference/index.rst
+++ b/doc/source/configuration/reference/index.rst
@@ -18,6 +18,7 @@ options.
    kolla
    kolla-ansible
    bifrost
+   overcloud-dib
    ironic-python-agent
    docker-registry
    seed-custom-containers
diff --git a/doc/source/configuration/reference/os-distribution.rst b/doc/source/configuration/reference/os-distribution.rst
index 4e86962b85eb6b0233105f4b70b4532b4c840c0f..7347494cd7a09f0ad82b1512f458ef3913989a38 100644
--- a/doc/source/configuration/reference/os-distribution.rst
+++ b/doc/source/configuration/reference/os-distribution.rst
@@ -1,3 +1,5 @@
+.. _os-distribution:
+
 ===============
 OS Distribution
 ===============
diff --git a/doc/source/configuration/reference/overcloud-dib.rst b/doc/source/configuration/reference/overcloud-dib.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d20e3d61e32870059b95bcb61c386cb4c0535086
--- /dev/null
+++ b/doc/source/configuration/reference/overcloud-dib.rst
@@ -0,0 +1,209 @@
+.. _overcloud-dib:
+
+===============================
+Overcloud host disk image build
+===============================
+
+This section covers configuration for building overcloud host disk images with
+Diskimage builder (DIB), which is available from the Yoga 12.0.0 release. This
+configuration is applied in ``${KAYOBE_CONFIG_PATH}/overcloud-dib.yml``.
+
+Enabling host disk image build
+==============================
+
+From the Yoga release, disk images for overcloud hosts can be built directly
+using Diskimage builder rather than through Bifrost. This is enabled with the
+following option:
+
+``overcloud_dib_build_host_images``
+    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.
+
+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.
+
+Overcloud root disk image configuration
+=======================================
+
+Kayobe uses Diskimage builder (DIB) to build root disk images that are deployed
+to overcloud hosts when they are provisioned. The following options configure
+how these images are built. Consult the
+:diskimage-builder-doc:`Diskimage-builder documentation <>` for further
+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.
+`Cloud-init <https://cloudinit.readthedocs.io/en/latest/>`__ is used to process
+the configuration drive built by Bifrost during provisioning.
+
+``overcloud_dib_host_images``
+    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
+    <https://galaxy.ansible.com/stackhpc/os-images>`__ role. Default is to
+    build an image named ``deployment_image`` configured with the
+    ``overcloud_dib_*`` variables defined below: ``{"name": "deployment_image",
+    "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 }}``.
+``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.
+``overcloud_dib_elements_extra``
+    List of additional DIB elements. Default is none.
+``overcloud_dib_elements``
+    List of DIB elements. Default is a combination of ``overcloud_dib_elements_default``
+    and ``overcloud_dib_elements_extra``.
+``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 }}"}``.
+``overcloud_dib_env_vars_extra``
+    DIB additional environment variables. Default is none.
+``overcloud_dib_env_vars``
+    DIB environment variables. Default is combination of
+    ``overcloud_dib_env_vars_default`` and
+    ``overcloud_dib_env_vars_extra``.
+``overcloud_dib_packages``
+    List of DIB packages to install. Default is to install no extra packages.
+``overcloud_dib_upper_constraints_file``
+    Upper constraints file for installing packages in the virtual environment
+    used for building overcloud host disk images. Default is ``{{
+    pip_upper_constraints_file }}``.
+
+Disk images are built with the following command:
+
+.. code-block:: console
+
+   (kayobe) $ kayobe overcloud host image build
+
+It is worth noting that images will not be rebuilt if they already exist. To
+force rebuilding images, it is necessary to use the ``--force-rebuild``
+argument.
+
+.. code-block:: console
+
+   (kayobe) $ kayobe overcloud host image build --force-rebuild
+
+Example: Adding an element
+--------------------------
+
+In the following, we extend the list of DIB elements to add the ``growpart``
+element:
+
+.. code-block:: yaml
+   :caption: ``dib.yml``
+
+   overcloud_dib_elements_extra:
+     - "growpart"
+
+Example: Building an XFS root filesystem image
+----------------------------------------------
+
+By default, DIB will format the image as ``ext4``. In some cases it might be
+useful to use XFS, for example when using the ``overlay`` Docker storage driver
+which can reach the maximum number of hardlinks allowed by ``ext4``.
+
+In DIB, we achieve this by setting the ``FS_TYPE`` environment variable to
+``xfs``.
+
+.. code-block:: yaml
+   :caption: ``dib.yml``
+
+   overcloud_dib_env_vars_extra:
+     FS_TYPE: "xfs"
+
+Example: Configuring a development user account
+-----------------------------------------------
+
+.. warning::
+
+   A development user account should not be used in production.
+
+When debugging a failed deployment, it can sometimes be necessary to allow
+access to the image via a preconfigured user account with a known password.
+This can be achieved via the :diskimage-builder-doc:`devuser
+<elements/devuser/README>` element.
+
+This example shows how to add the ``devuser`` element, and configure a username
+and password for an account that has passwordless sudo:
+
+.. code-block:: yaml
+   :caption: ``dib.yml``
+
+   overcloud_dib_elements_extra:
+     - "devuser"
+
+   overcloud_dib_env_vars_extra:
+     DIB_DEV_USER_USERNAME: "devuser"
+     DIB_DEV_USER_PASSWORD: "correct horse battery staple"
+     DIB_DEV_USER_PWDLESS_SUDO: "yes"
+
+Alternatively, the :diskimage-builder-doc:`dynamic-login element
+<elements/dynamic-login/README>` can be used to authorize SSH keys by appending
+them to the kernel arguments.
+
+Example: Installing a package
+-----------------------------
+
+It can be necessary to install additional packages in the root disk image.
+Rather than needing to write a custom DIB element, we can use the
+``overcloud_dib_packages`` variable. For example, to install the
+``biosdevname`` package:
+
+.. code-block:: yaml
+   :caption: ``dib.yml``
+
+   overcloud_dib_packages:
+     - "biosdevname"
+
+Example: Building multiple images
+---------------------------------
+
+It can be necessary to build multiple images to support the various types of
+hardware present in a deployment or the different functions performed by
+overcloud hosts. This can be configured with the ``overcloud_dib_host_images``
+variable, using a format accepted by the `stackhpc.os-images
+<https://galaxy.ansible.com/stackhpc/os-images>`__ role. Note that image names
+should not include the file extension.  For example, to build a second image
+with a development user account and the ``biosdevname`` package:
+
+.. code-block:: yaml
+   :caption: ``dib.yml``
+
+   overcloud_dib_host_images:
+     - name: "deployment_image"
+       elements: "{{ overcloud_dib_elements }}"
+       env: "{{ overcloud_dib_env_vars }}"
+       packages: "{{ overcloud_dib_packages }}"
+     - name: "debug_deployment_image"
+       elements: "{{ overcloud_dib_elements + ['devuser'] }}"
+       env: "{{ overcloud_dib_env_vars | combine(devuser_env_vars) }}"
+       packages: "{{ overcloud_dib_packages + ['biosdevname'] }}"
+
+   devuser_env_vars:
+     DIB_DEV_USER_USERNAME: "devuser"
+     DIB_DEV_USER_PASSWORD: "correct horse battery staple"
+     DIB_DEV_USER_PWDLESS_SUDO: "yes"
+
+Running the ``kayobe overcloud host image build`` command with this
+configuration will create two images: ``deployment_image.qcow2`` and
+``debug_deployment_image.qcow2``.
+
+Disk image deployment configuration
+===================================
+
+See :ref:`disk image deployment configuration in
+Bifrost<configuration-bifrost-image-deployment-config>` for how to configure
+the root disk image to be used to provision each host.
diff --git a/doc/source/deployment.rst b/doc/source/deployment.rst
index b1d28edcc90dbe1dc8e276b189c68a4d0009a607..38b84549503a6da93972c4b57e2c56b26ec05622 100644
--- a/doc/source/deployment.rst
+++ b/doc/source/deployment.rst
@@ -172,7 +172,8 @@ At this point the seed services need to be deployed on the seed VM.  These
 services are deployed in the ``bifrost_deploy`` container.
 
 This command will also build the Operating System image that will be used to
-deploy the overcloud nodes using Disk Image Builder (DIB).
+deploy the overcloud nodes using Disk Image Builder (DIB), unless
+``overcloud_dib_build_host_images`` is set to ``True``.
 
 To deploy the seed services in containers::
 
@@ -217,6 +218,27 @@ rebuilding images, use the ``--force-rebuild`` argument.
    See :ref:`here <configuration-ipa-build>` for information on how to
    configure the IPA image build process.
 
+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.
+
+Host disk images are deployed on overcloud hosts during provisioning. To build
+host disk images::
+
+    (kayobe) $ kayobe overcloud host image build
+
+If images have been built previously, they will not be rebuilt. To force
+rebuilding images, use the ``--force-rebuild`` argument.
+
+.. seealso::
+
+   See :ref:`here <overcloud-dib>` for information on how to configure the
+   overcloud host disk image build process.
+
 Accessing the Seed via SSH (Optional)
 -------------------------------------
 
diff --git a/etc/kayobe/overcloud-dib.yml b/etc/kayobe/overcloud-dib.yml
index 0a95c97e2daa122f056167c3a47224602c219f95..54418e86cec5dd3c61cbc918d1e84bf9e9ffa317 100644
--- a/etc/kayobe/overcloud-dib.yml
+++ b/etc/kayobe/overcloud-dib.yml
@@ -10,6 +10,14 @@
 # is False. 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
+# an image in a format accepted by the stackhpc.os-images role. Default is to
+# build an image named "deployment_image" configured with the overcloud_dib_*
+# variables defined below: {"name": "deployment_image", "elements": "{{
+# overcloud_dib_elements }}", "env": "{{ overcloud_dib_env_vars }}",
+# "packages": "{{ overcloud_dib_packages }}"}.
+#overcloud_dib_host_images:
+
 # DIB base OS element. Default is {{ os_distribution }}.
 #overcloud_dib_os_element:
 
diff --git a/releasenotes/notes/dib-host-image-4fe8b1bf078f2d27.yaml b/releasenotes/notes/dib-host-image-4fe8b1bf078f2d27.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..485937990de94ecaf53a61be1929b8c4b5ef4ace
--- /dev/null
+++ b/releasenotes/notes/dib-host-image-4fe8b1bf078f2d27.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    Adds support for building overcloud root disk images directly with DIB
+    rather than through Bifrost. This includes support for building multiple
+    images, each with a different configuration. See `story 2002098
+    <https://storyboard.openstack.org/#!/story/2002098>` for details.