diff --git a/playbooks/kayobe-seed-base/overrides.yml.j2 b/playbooks/kayobe-seed-base/overrides.yml.j2
index 5fe3c427925f5933bd87abef9807a17757abbc59..806430301d3abc4a8c90679b26e3391d3235528e 100644
--- a/playbooks/kayobe-seed-base/overrides.yml.j2
+++ b/playbooks/kayobe-seed-base/overrides.yml.j2
@@ -32,3 +32,11 @@ pip_trusted_hosts:
 # are using for SSH to be removed. Use a dummy interface.
 aio_bridge_ports:
   - dummy1
+
+# Build seed deployment images (IPA) with extra-hardware element
+ipa_build_images: {{ build_images }}
+ipa_build_dib_elements_extra:
+  - "extra-hardware"
+
+# Build overcloud host image.
+overcloud_dib_build_host_images: {{ build_images }}
diff --git a/playbooks/kayobe-seed-base/pre.yml b/playbooks/kayobe-seed-base/pre.yml
index 7db70d533dad83833991ea3400c91877f54cc928..6eef5c6418db3c276f90f1074648874647754e39 100644
--- a/playbooks/kayobe-seed-base/pre.yml
+++ b/playbooks/kayobe-seed-base/pre.yml
@@ -25,16 +25,18 @@
         src: globals.yml.j2
         dest: "{{ kayobe_config_src_dir }}/etc/kayobe/kolla/globals.yml"
 
-    - name: Ensure bifrost overrides directory exists
-      file:
-        path: "{{ kayobe_config_src_dir }}/etc/kayobe/kolla/config/bifrost"
-        state: "directory"
+    - block:
+        - name: Ensure bifrost overrides directory exists
+          file:
+            path: "{{ kayobe_config_src_dir }}/etc/kayobe/kolla/config/bifrost"
+            state: "directory"
 
-    # NOTE(mgoddard): Use dib.yml, since it takes precedence over bifrost.yml.
-    - name: Ensure bifrost overrides file exists
-      template:
-        src: bifrost-overrides.yml.j2
-        dest: "{{ kayobe_config_src_dir }}/etc/kayobe/kolla/config/bifrost/dib.yml"
+        # NOTE(mgoddard): Use dib.yml, since it takes precedence over bifrost.yml.
+        - name: Ensure bifrost overrides file exists
+          template:
+            src: bifrost-overrides.yml.j2
+            dest: "{{ kayobe_config_src_dir }}/etc/kayobe/kolla/config/bifrost/dib.yml"
+      when: not build_images
 
     - name: Ensure kayobe is installed
       shell:
diff --git a/playbooks/kayobe-seed-base/run.yml b/playbooks/kayobe-seed-base/run.yml
index 7a5c3eefac2c95b2ec026d6b6a203ad551f5add1..814db8386450ca52bdd223a9ce30136ae272d8e4 100644
--- a/playbooks/kayobe-seed-base/run.yml
+++ b/playbooks/kayobe-seed-base/run.yml
@@ -10,21 +10,22 @@
         cmd: "{{ kayobe_src_dir }}/dev/seed-deploy.sh &> {{ logs_dir }}/ansible/seed-deploy"
         executable: /bin/bash
 
-    # NOTE(mgoddard): The stack user cannot read the kolla source code, which
-    # lives in the zuul user's home directory. Copy it to a readable location.
-    - name: Create a readable copy of kolla source code
-      become: true
-      copy:
-        src: "{{ ansible_env.PWD ~ '/' ~ zuul.projects['opendev.org/openstack/kolla'].src_dir }}"
-        dest: /tmp
-        owner: stack
-        group: stack
-        remote_src: true
+    - block:
+        # NOTE(mgoddard): The stack user cannot read the kolla source code, which
+        # lives in the zuul user's home directory. Copy it to a readable location.
+        - name: Create a readable copy of kolla source code
+          become: true
+          copy:
+            src: "{{ ansible_env.PWD ~ '/' ~ zuul.projects['opendev.org/openstack/kolla'].src_dir }}"
+            dest: /tmp
+            owner: stack
+            group: stack
+            remote_src: true
 
-    - name: Ensure base container image is built
-      shell:
-        cmd: >
-          source {{ kayobe_src_dir }}/dev/environment-setup.sh &&
-          kayobe seed container image build ^base &> {{ logs_dir }}/ansible/container-image-build
-        executable: /bin/bash
-      when: ansible_os_family == 'RedHat'
+        - name: Ensure base container image is built
+          shell:
+            cmd: >
+              source {{ kayobe_src_dir }}/dev/environment-setup.sh &&
+              kayobe seed container image build ^base &> {{ logs_dir }}/ansible/container-image-build
+            executable: /bin/bash
+      when: build_images
diff --git a/zuul.d/jobs.yaml b/zuul.d/jobs.yaml
index a16accd9c332a6073fb6b787a2f1a2f06fef5608..76bed1888441aa48c7067bfcda730856b263ebb1 100644
--- a/zuul.d/jobs.yaml
+++ b/zuul.d/jobs.yaml
@@ -161,6 +161,8 @@
     pre-run: playbooks/kayobe-seed-base/pre.yml
     run: playbooks/kayobe-seed-base/run.yml
     timeout: 5400
+    vars:
+      build_images: false
 
 - job:
     name: kayobe-seed-centos8s
@@ -177,6 +179,32 @@
     parent: kayobe-seed-base
     nodeset: kayobe-ubuntu-focal
 
+- job:
+    name: kayobe-seed-images-base
+    parent: kayobe-seed-base
+    description: |
+      Base job for testing seed image builds.
+
+      Configures the primary VM as a seed.
+    voting: false
+    vars:
+      build_images: true
+
+- job:
+    name: kayobe-seed-images-centos8s
+    parent: kayobe-seed-images-base
+    nodeset: kayobe-centos8s
+
+- job:
+    name: kayobe-seed-images-rocky8
+    parent: kayobe-seed-images-base
+    nodeset: kayobe-rocky8
+
+- job:
+    name: kayobe-seed-images-ubuntu-focal
+    parent: kayobe-seed-images-base
+    nodeset: kayobe-ubuntu-focal
+
 - job:
     name: kayobe-overcloud-host-configure-base
     parent: kayobe-overcloud-base
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index 02311ad642a1eda6525d0a066faadddcabe97025..fabc08984f4a82e2187a157723f189221cf288e1 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -22,6 +22,7 @@
         - kayobe-seed-centos8s
         - kayobe-seed-rocky8
         - kayobe-seed-ubuntu-focal
+        - kayobe-seed-images-centos8s
         - kayobe-seed-upgrade-centos8s
         - kayobe-seed-upgrade-ubuntu-focal
         - kayobe-seed-vm-centos8s
@@ -57,3 +58,8 @@
         - kayobe-infra-vm-centos8s
         - kayobe-infra-vm-rocky8
         - kayobe-infra-vm-ubuntu-focal
+
+    experimental:
+      jobs:
+        - kayobe-seed-images-rocky8
+        - kayobe-seed-images-ubuntu-focal