diff --git a/ansible/infra-vm-host-configure.yml b/ansible/infra-vm-host-configure.yml
new file mode 100644
index 000000000..ce7b25c8e
--- /dev/null
+++ b/ansible/infra-vm-host-configure.yml
@@ -0,0 +1,24 @@
+---
+- import_playbook: "ssh-known-host.yml"
+- import_playbook: "kayobe-ansible-user.yml"
+- import_playbook: "proxy.yml"
+- import_playbook: "apt.yml"
+- import_playbook: "dnf.yml"
+- import_playbook: "pip.yml"
+- import_playbook: "kayobe-target-venv.yml"
+- import_playbook: "wipe-disks.yml"
+- import_playbook: "users.yml"
+- import_playbook: "dev-tools.yml"
+- import_playbook: "disable-selinux.yml"
+- import_playbook: "network.yml"
+- import_playbook: "firewall.yml"
+- import_playbook: "tuned.yml"
+- import_playbook: "sysctl.yml"
+- import_playbook: "disable-glean.yml"
+- import_playbook: "disable-cloud-init.yml"
+- import_playbook: "time.yml"
+- import_playbook: "mdadm.yml"
+- import_playbook: "luks.yml"
+- import_playbook: "lvm.yml"
+- import_playbook: "docker-devicemapper.yml"
+- import_playbook: "docker.yml"
diff --git a/ansible/overcloud-host-configure.yml b/ansible/overcloud-host-configure.yml
new file mode 100644
index 000000000..31587891b
--- /dev/null
+++ b/ansible/overcloud-host-configure.yml
@@ -0,0 +1,26 @@
+---
+- import_playbook: "ssh-known-host.yml"
+- import_playbook: "kayobe-ansible-user.yml"
+- import_playbook: "proxy.yml"
+- import_playbook: "apt.yml"
+- import_playbook: "dnf.yml"
+- import_playbook: "pip.yml"
+- import_playbook: "kayobe-target-venv.yml"
+- import_playbook: "wipe-disks.yml"
+- import_playbook: "users.yml"
+- import_playbook: "dev-tools.yml"
+- import_playbook: "disable-selinux.yml"
+- import_playbook: "network.yml"
+- import_playbook: "firewall.yml"
+- import_playbook: "tuned.yml"
+- import_playbook: "sysctl.yml"
+- import_playbook: "disable-glean.yml"
+- import_playbook: "disable-cloud-init.yml"
+- import_playbook: "time.yml"
+- import_playbook: "mdadm.yml"
+- import_playbook: "luks.yml"
+- import_playbook: "lvm.yml"
+- import_playbook: "docker-devicemapper.yml"
+- import_playbook: "kolla-ansible-user.yml"
+- import_playbook: "kolla-pip.yml"
+- import_playbook: "kolla-target-venv.yml"
diff --git a/ansible/overcloud-host-upgrade.yml b/ansible/overcloud-host-upgrade.yml
new file mode 100644
index 000000000..4564abec6
--- /dev/null
+++ b/ansible/overcloud-host-upgrade.yml
@@ -0,0 +1,5 @@
+---
+- import_playbook: "kayobe-target-venv.yml"
+- import_playbook: "kolla-target-venv.yml"
+- import_playbook: "overcloud-docker-sdk-upgrade.yml"
+- import_playbook: "overcloud-etc-hosts-fixup.yml"
diff --git a/ansible/seed-host-configure.yml b/ansible/seed-host-configure.yml
new file mode 100644
index 000000000..4a89f4f09
--- /dev/null
+++ b/ansible/seed-host-configure.yml
@@ -0,0 +1,27 @@
+---
+- import_playbook: "ssh-known-host.yml"
+- import_playbook: "kayobe-ansible-user.yml"
+- import_playbook: "proxy.yml"
+- import_playbook: "apt.yml"
+- import_playbook: "dnf.yml"
+- import_playbook: "pip.yml"
+- import_playbook: "kayobe-target-venv.yml"
+- import_playbook: "wipe-disks.yml"
+- import_playbook: "users.yml"
+- import_playbook: "dev-tools.yml"
+- import_playbook: "disable-selinux.yml"
+- import_playbook: "network.yml"
+- import_playbook: "firewall.yml"
+- import_playbook: "tuned.yml"
+- import_playbook: "sysctl.yml"
+- import_playbook: "ip-routing.yml"
+- import_playbook: "snat.yml"
+- import_playbook: "disable-glean.yml"
+- import_playbook: "time.yml"
+- import_playbook: "mdadm.yml"
+- import_playbook: "luks.yml"
+- import_playbook: "lvm.yml"
+- import_playbook: "docker-devicemapper.yml"
+- import_playbook: "kolla-ansible-user.yml"
+- import_playbook: "kolla-pip.yml"
+- import_playbook: "kolla-target-venv.yml"
diff --git a/ansible/seed-host-upgrade.yml b/ansible/seed-host-upgrade.yml
new file mode 100644
index 000000000..d803c397e
--- /dev/null
+++ b/ansible/seed-host-upgrade.yml
@@ -0,0 +1,3 @@
+---
+- import_playbook: "kayobe-target-venv.yml"
+- import_playbook: "kolla-target-venv.yml"
diff --git a/ansible/seed-hypervisor-host-configure.yml b/ansible/seed-hypervisor-host-configure.yml
new file mode 100644
index 000000000..86c70623b
--- /dev/null
+++ b/ansible/seed-hypervisor-host-configure.yml
@@ -0,0 +1,22 @@
+---
+- import_playbook: "ssh-known-host.yml"
+- import_playbook: "kayobe-ansible-user.yml"
+- import_playbook: "proxy.yml"
+- import_playbook: "apt.yml"
+- import_playbook: "dnf.yml"
+- import_playbook: "pip.yml"
+- import_playbook: "kayobe-target-venv.yml"
+- import_playbook: "wipe-disks.yml"
+- import_playbook: "users.yml"
+- import_playbook: "dev-tools.yml"
+- import_playbook: "network.yml"
+- import_playbook: "firewall.yml"
+- import_playbook: "tuned.yml"
+- import_playbook: "sysctl.yml"
+- import_playbook: "ip-routing.yml"
+- import_playbook: "snat.yml"
+- import_playbook: "time.yml"
+- import_playbook: "mdadm.yml"
+- import_playbook: "luks.yml"
+- import_playbook: "lvm.yml"
+- import_playbook: "seed-hypervisor-libvirt-host.yml"
diff --git a/ansible/wipe-disks.yml b/ansible/wipe-disks.yml
index 8ade2328e..a833f3c17 100644
--- a/ansible/wipe-disks.yml
+++ b/ansible/wipe-disks.yml
@@ -11,8 +11,15 @@
   hosts: seed-hypervisor:seed:overcloud:infra-vms
   tags:
     - wipe-disks
-  roles:
-    - role: stackhpc.luks
-      vars:
-        luks_action: teardown-unmounted
-    - role: wipe-disks
+  tasks:
+    - block:
+        - name: Tear down unmounted LUKS devices
+          include_role:
+            name: stackhpc.luks
+          vars:
+            luks_action: teardown-unmounted
+
+        - name: Wipe disks
+          include_role:
+            name: wipe-disks
+      when: wipe_disks | default(false) | bool
diff --git a/kayobe/cli/commands.py b/kayobe/cli/commands.py
index 0ba90f2aa..788d493b5 100644
--- a/kayobe/cli/commands.py
+++ b/kayobe/cli/commands.py
@@ -442,17 +442,12 @@ class SeedHypervisorHostConfigure(KollaAnsibleMixin, KayobeAnsibleMixin,
         self.run_kayobe_playbooks(parsed_args, playbooks,
                                   limit="seed-hypervisor")
 
-        playbooks = _build_playbook_list(
-            "ssh-known-host", "kayobe-ansible-user", "proxy",
-            "apt", "dnf", "pip", "kayobe-target-venv")
+        kwargs = {}
         if parsed_args.wipe_disks:
-            playbooks += _build_playbook_list("wipe-disks")
-        playbooks += _build_playbook_list(
-            "users", "dev-tools", "network", "firewall", "tuned", "sysctl",
-            "ip-routing", "snat", "time", "mdadm", "luks", "lvm",
-            "seed-hypervisor-libvirt-host")
+            kwargs["extra_vars"] = {"wipe_disks": True}
+        playbooks = _build_playbook_list("seed-hypervisor-host-configure")
         self.run_kayobe_playbooks(parsed_args, playbooks,
-                                  limit="seed-hypervisor")
+                                  limit="seed-hypervisor", **kwargs)
 
 
 class SeedHypervisorHostPackageUpdate(KayobeAnsibleMixin, VaultMixin, Command):
@@ -600,17 +595,12 @@ class SeedHostConfigure(KollaAnsibleMixin, KayobeAnsibleMixin, VaultMixin,
         self.run_kayobe_playbooks(parsed_args, playbooks, limit="seed")
 
         # Run kayobe playbooks.
-        playbooks = _build_playbook_list(
-            "ssh-known-host", "kayobe-ansible-user", "proxy",
-            "apt", "dnf", "pip", "kayobe-target-venv")
+        kwargs = {}
         if parsed_args.wipe_disks:
-            playbooks += _build_playbook_list("wipe-disks")
-        playbooks += _build_playbook_list(
-            "users", "dev-tools", "disable-selinux", "network", "firewall",
-            "tuned", "sysctl", "ip-routing", "snat", "disable-glean", "time",
-            "mdadm", "luks", "lvm", "docker-devicemapper",
-            "kolla-ansible-user", "kolla-pip", "kolla-target-venv")
-        self.run_kayobe_playbooks(parsed_args, playbooks, limit="seed")
+            kwargs["extra_vars"] = {"wipe_disks": True}
+        playbooks = _build_playbook_list("seed-host-configure")
+        self.run_kayobe_playbooks(parsed_args, playbooks, limit="seed",
+                                  **kwargs)
 
         self.generate_kolla_ansible_config(parsed_args, service_config=False)
 
@@ -685,8 +675,7 @@ class SeedHostUpgrade(KollaAnsibleMixin, KayobeAnsibleMixin, VaultMixin,
 
     def take_action(self, parsed_args):
         self.app.LOG.debug("Upgrading seed host services")
-        playbooks = _build_playbook_list(
-            "kayobe-target-venv", "kolla-target-venv")
+        playbooks = _build_playbook_list("seed-host-upgrade")
         self.run_kayobe_playbooks(parsed_args, playbooks, limit="seed")
 
 
@@ -906,16 +895,12 @@ class InfraVMHostConfigure(KayobeAnsibleMixin, VaultMixin,
         self.run_kayobe_playbooks(parsed_args, playbooks, limit="infra-vms")
 
         # Kayobe playbooks.
-        playbooks = _build_playbook_list(
-            "ssh-known-host", "kayobe-ansible-user", "proxy",
-            "apt", "dnf", "pip", "kayobe-target-venv")
+        kwargs = {}
         if parsed_args.wipe_disks:
-            playbooks += _build_playbook_list("wipe-disks")
-        playbooks += _build_playbook_list(
-            "users", "dev-tools", "disable-selinux", "network", "firewall",
-            "tuned", "sysctl", "disable-glean", "disable-cloud-init", "time",
-            "mdadm", "luks", "lvm", "docker-devicemapper", "docker")
-        self.run_kayobe_playbooks(parsed_args, playbooks, limit="infra-vms")
+            kwargs["extra_vars"] = {"wipe_disks": True}
+        playbooks = _build_playbook_list("infra-vm-host-configure")
+        self.run_kayobe_playbooks(parsed_args, playbooks, limit="infra-vms",
+                                  **kwargs)
 
 
 class InfraVMHostPackageUpdate(KayobeAnsibleMixin, VaultMixin, Command):
@@ -1159,17 +1144,12 @@ class OvercloudHostConfigure(KollaAnsibleMixin, KayobeAnsibleMixin, VaultMixin,
         self.run_kayobe_playbooks(parsed_args, playbooks, limit="overcloud")
 
         # Kayobe playbooks.
-        playbooks = _build_playbook_list(
-            "ssh-known-host", "kayobe-ansible-user", "proxy",
-            "apt", "dnf", "pip", "kayobe-target-venv")
+        kwargs = {}
         if parsed_args.wipe_disks:
-            playbooks += _build_playbook_list("wipe-disks")
-        playbooks += _build_playbook_list(
-            "users", "dev-tools", "disable-selinux", "network", "firewall",
-            "tuned", "sysctl", "disable-glean", "disable-cloud-init", "time",
-            "mdadm", "luks", "lvm", "docker-devicemapper",
-            "kolla-ansible-user", "kolla-pip", "kolla-target-venv")
-        self.run_kayobe_playbooks(parsed_args, playbooks, limit="overcloud")
+            kwargs["extra_vars"] = {"wipe_disks": True}
+        playbooks = _build_playbook_list("overcloud-host-configure")
+        self.run_kayobe_playbooks(parsed_args, playbooks, limit="overcloud",
+                                  **kwargs)
 
         self.generate_kolla_ansible_config(parsed_args, service_config=False)
 
@@ -1238,9 +1218,7 @@ class OvercloudHostUpgrade(KollaAnsibleMixin, KayobeAnsibleMixin, VaultMixin,
 
     def take_action(self, parsed_args):
         self.app.LOG.debug("Upgrading overcloud host services")
-        playbooks = _build_playbook_list(
-            "kayobe-target-venv", "kolla-target-venv",
-            "overcloud-docker-sdk-upgrade", "overcloud-etc-hosts-fixup")
+        playbooks = _build_playbook_list("overcloud-host-upgrade")
         self.run_kayobe_playbooks(parsed_args, playbooks, limit="overcloud")
 
 
diff --git a/kayobe/tests/unit/cli/test_commands.py b/kayobe/tests/unit/cli/test_commands.py
index 6ba5aa11d..dbb402e1d 100644
--- a/kayobe/tests/unit/cli/test_commands.py
+++ b/kayobe/tests/unit/cli/test_commands.py
@@ -316,35 +316,42 @@ class TestCase(unittest.TestCase):
             mock.call(
                 mock.ANY,
                 [
-                    utils.get_data_files_path("ansible", "ssh-known-host.yml"),
                     utils.get_data_files_path(
-                        "ansible", "kayobe-ansible-user.yml"),
-                    utils.get_data_files_path("ansible", "proxy.yml"),
-                    utils.get_data_files_path("ansible", "apt.yml"),
-                    utils.get_data_files_path("ansible", "dnf.yml"),
-                    utils.get_data_files_path("ansible", "pip.yml"),
-                    utils.get_data_files_path(
-                        "ansible", "kayobe-target-venv.yml"),
-                    utils.get_data_files_path("ansible", "users.yml"),
-                    utils.get_data_files_path("ansible", "dev-tools.yml"),
-                    utils.get_data_files_path("ansible", "network.yml"),
-                    utils.get_data_files_path("ansible", "firewall.yml"),
-                    utils.get_data_files_path("ansible", "tuned.yml"),
-                    utils.get_data_files_path("ansible", "sysctl.yml"),
-                    utils.get_data_files_path("ansible", "ip-routing.yml"),
-                    utils.get_data_files_path("ansible", "snat.yml"),
-                    utils.get_data_files_path("ansible", "time.yml"),
-                    utils.get_data_files_path("ansible", "mdadm.yml"),
-                    utils.get_data_files_path("ansible", "luks.yml"),
-                    utils.get_data_files_path("ansible", "lvm.yml"),
-                    utils.get_data_files_path(
-                        "ansible", "seed-hypervisor-libvirt-host.yml"),
+                        "ansible", "seed-hypervisor-host-configure.yml"),
                 ],
                 limit="seed-hypervisor",
             ),
         ]
         self.assertEqual(expected_calls, mock_run.call_args_list)
 
+    @mock.patch.object(commands.KayobeAnsibleMixin,
+                       "run_kayobe_playbooks")
+    def test_seed_hypervisor_host_configure_wipe_disks(self, mock_run):
+        command = commands.SeedHypervisorHostConfigure(TestApp(), [])
+        parser = command.get_parser("test")
+        parsed_args = parser.parse_args(["--wipe-disks"])
+
+        result = command.run(parsed_args)
+        self.assertEqual(0, result)
+
+        expected_calls = [
+            mock.call(
+                mock.ANY,
+                [utils.get_data_files_path("ansible", "ip-allocation.yml")],
+                limit="seed-hypervisor",
+            ),
+            mock.call(
+                mock.ANY,
+                [
+                    utils.get_data_files_path(
+                        "ansible", "seed-hypervisor-host-configure.yml"),
+                ],
+                limit="seed-hypervisor",
+                extra_vars={"wipe_disks": True},
+            ),
+        ]
+        self.assertEqual(expected_calls, mock_run.call_args_list)
+
     @mock.patch.object(commands.KayobeAnsibleMixin,
                        "run_kayobe_playbooks")
     def test_seed_hypervisor_host_command_run(self, mock_run):
@@ -492,37 +499,8 @@ class TestCase(unittest.TestCase):
             mock.call(
                 mock.ANY,
                 [
-                    utils.get_data_files_path("ansible", "ssh-known-host.yml"),
                     utils.get_data_files_path(
-                        "ansible", "kayobe-ansible-user.yml"),
-                    utils.get_data_files_path("ansible", "proxy.yml"),
-                    utils.get_data_files_path("ansible", "apt.yml"),
-                    utils.get_data_files_path("ansible", "dnf.yml"),
-                    utils.get_data_files_path("ansible", "pip.yml"),
-                    utils.get_data_files_path(
-                        "ansible", "kayobe-target-venv.yml"),
-                    utils.get_data_files_path("ansible", "users.yml"),
-                    utils.get_data_files_path("ansible", "dev-tools.yml"),
-                    utils.get_data_files_path(
-                        "ansible", "disable-selinux.yml"),
-                    utils.get_data_files_path("ansible", "network.yml"),
-                    utils.get_data_files_path("ansible", "firewall.yml"),
-                    utils.get_data_files_path("ansible", "tuned.yml"),
-                    utils.get_data_files_path("ansible", "sysctl.yml"),
-                    utils.get_data_files_path("ansible", "ip-routing.yml"),
-                    utils.get_data_files_path("ansible", "snat.yml"),
-                    utils.get_data_files_path("ansible", "disable-glean.yml"),
-                    utils.get_data_files_path("ansible", "time.yml"),
-                    utils.get_data_files_path("ansible", "mdadm.yml"),
-                    utils.get_data_files_path("ansible", "luks.yml"),
-                    utils.get_data_files_path("ansible", "lvm.yml"),
-                    utils.get_data_files_path("ansible",
-                                              "docker-devicemapper.yml"),
-                    utils.get_data_files_path(
-                        "ansible", "kolla-ansible-user.yml"),
-                    utils.get_data_files_path("ansible", "kolla-pip.yml"),
-                    utils.get_data_files_path(
-                        "ansible", "kolla-target-venv.yml"),
+                        "ansible", "seed-host-configure.yml"),
                 ],
                 limit="seed",
             ),
@@ -559,6 +537,68 @@ class TestCase(unittest.TestCase):
         ]
         self.assertEqual(expected_calls, mock_kolla_run.call_args_list)
 
+    @mock.patch.object(commands.KayobeAnsibleMixin,
+                       "run_kayobe_playbooks")
+    @mock.patch.object(commands.KollaAnsibleMixin,
+                       "run_kolla_ansible_seed")
+    def test_seed_host_configure_wipe_disks(self, mock_kolla_run, mock_run):
+        command = commands.SeedHostConfigure(TestApp(), [])
+        parser = command.get_parser("test")
+        parsed_args = parser.parse_args(["--wipe-disks"])
+
+        result = command.run(parsed_args)
+        self.assertEqual(0, result)
+
+        expected_calls = [
+            mock.call(
+                mock.ANY,
+                [utils.get_data_files_path("ansible", "ip-allocation.yml")],
+                limit="seed",
+            ),
+            mock.call(
+                mock.ANY,
+                [
+                    utils.get_data_files_path(
+                        "ansible", "seed-host-configure.yml"),
+                ],
+                limit="seed",
+                extra_vars={"wipe_disks": True},
+            ),
+            mock.call(
+                mock.ANY,
+                [utils.get_data_files_path("ansible", "kolla-ansible.yml")],
+                tags="config",
+                ignore_limit=True,
+            ),
+            mock.call(
+                mock.ANY,
+                [
+                    utils.get_data_files_path("ansible", "docker.yml"),
+                ],
+                limit="seed",
+            ),
+            mock.call(
+                mock.ANY,
+                [
+                    utils.get_data_files_path("ansible",
+                                              "docker-registry.yml"),
+                ],
+                limit="seed",
+                extra_vars={'kayobe_action': 'deploy'},
+            ),
+        ]
+        print(expected_calls)
+        print(mock_run.call_args_list)
+        self.assertEqual(expected_calls, mock_run.call_args_list)
+
+        expected_calls = [
+            mock.call(
+                mock.ANY,
+                "bootstrap-servers",
+            ),
+        ]
+        self.assertEqual(expected_calls, mock_kolla_run.call_args_list)
+
     @mock.patch.object(commands.KayobeAnsibleMixin,
                        "run_kayobe_playbooks")
     def test_seed_host_command_run(self, mock_run):
@@ -678,9 +718,7 @@ class TestCase(unittest.TestCase):
                 mock.ANY,
                 [
                     utils.get_data_files_path(
-                        "ansible", "kayobe-target-venv.yml"),
-                    utils.get_data_files_path(
-                        "ansible", "kolla-target-venv.yml"),
+                        "ansible", "seed-host-upgrade.yml"),
                 ],
                 limit="seed",
             ),
@@ -984,39 +1022,42 @@ class TestCase(unittest.TestCase):
             mock.call(
                 mock.ANY,
                 [
-                    utils.get_data_files_path("ansible", "ssh-known-host.yml"),
                     utils.get_data_files_path(
-                        "ansible", "kayobe-ansible-user.yml"),
-                    utils.get_data_files_path("ansible", "proxy.yml"),
-                    utils.get_data_files_path("ansible", "apt.yml"),
-                    utils.get_data_files_path("ansible", "dnf.yml"),
-                    utils.get_data_files_path("ansible", "pip.yml"),
-                    utils.get_data_files_path(
-                        "ansible", "kayobe-target-venv.yml"),
-                    utils.get_data_files_path("ansible", "users.yml"),
-                    utils.get_data_files_path("ansible", "dev-tools.yml"),
-                    utils.get_data_files_path(
-                        "ansible", "disable-selinux.yml"),
-                    utils.get_data_files_path("ansible", "network.yml"),
-                    utils.get_data_files_path("ansible", "firewall.yml"),
-                    utils.get_data_files_path("ansible", "tuned.yml"),
-                    utils.get_data_files_path("ansible", "sysctl.yml"),
-                    utils.get_data_files_path("ansible", "disable-glean.yml"),
-                    utils.get_data_files_path(
-                        "ansible", "disable-cloud-init.yml"),
-                    utils.get_data_files_path("ansible", "time.yml"),
-                    utils.get_data_files_path("ansible", "mdadm.yml"),
-                    utils.get_data_files_path("ansible", "luks.yml"),
-                    utils.get_data_files_path("ansible", "lvm.yml"),
-                    utils.get_data_files_path("ansible",
-                                              "docker-devicemapper.yml"),
-                    utils.get_data_files_path("ansible", "docker.yml"),
+                        "ansible", "infra-vm-host-configure.yml"),
                 ],
                 limit="infra-vms",
             ),
         ]
         self.assertEqual(expected_calls, mock_run.call_args_list)
 
+    @mock.patch.object(commands.KayobeAnsibleMixin,
+                       "run_kayobe_playbooks")
+    def test_infra_vm_host_configure_wipe_disks(self, mock_run):
+        command = commands.InfraVMHostConfigure(TestApp(), [])
+        parser = command.get_parser("test")
+        parsed_args = parser.parse_args(["--wipe-disks"])
+
+        result = command.run(parsed_args)
+        self.assertEqual(0, result)
+
+        expected_calls = [
+            mock.call(
+                mock.ANY,
+                [utils.get_data_files_path("ansible", "ip-allocation.yml")],
+                limit="infra-vms",
+            ),
+            mock.call(
+                mock.ANY,
+                [
+                    utils.get_data_files_path(
+                        "ansible", "infra-vm-host-configure.yml"),
+                ],
+                limit="infra-vms",
+                extra_vars={"wipe_disks": True},
+            ),
+        ]
+        self.assertEqual(expected_calls, mock_run.call_args_list)
+
     @mock.patch.object(commands.KayobeAnsibleMixin,
                        "run_kayobe_playbooks")
     def test_infra_vm_host_upgrade(self, mock_run):
@@ -1264,37 +1305,8 @@ class TestCase(unittest.TestCase):
             mock.call(
                 mock.ANY,
                 [
-                    utils.get_data_files_path("ansible", "ssh-known-host.yml"),
                     utils.get_data_files_path(
-                        "ansible", "kayobe-ansible-user.yml"),
-                    utils.get_data_files_path("ansible", "proxy.yml"),
-                    utils.get_data_files_path("ansible", "apt.yml"),
-                    utils.get_data_files_path("ansible", "dnf.yml"),
-                    utils.get_data_files_path("ansible", "pip.yml"),
-                    utils.get_data_files_path(
-                        "ansible", "kayobe-target-venv.yml"),
-                    utils.get_data_files_path("ansible", "users.yml"),
-                    utils.get_data_files_path("ansible", "dev-tools.yml"),
-                    utils.get_data_files_path(
-                        "ansible", "disable-selinux.yml"),
-                    utils.get_data_files_path("ansible", "network.yml"),
-                    utils.get_data_files_path("ansible", "firewall.yml"),
-                    utils.get_data_files_path("ansible", "tuned.yml"),
-                    utils.get_data_files_path("ansible", "sysctl.yml"),
-                    utils.get_data_files_path("ansible", "disable-glean.yml"),
-                    utils.get_data_files_path(
-                        "ansible", "disable-cloud-init.yml"),
-                    utils.get_data_files_path("ansible", "time.yml"),
-                    utils.get_data_files_path("ansible", "mdadm.yml"),
-                    utils.get_data_files_path("ansible", "luks.yml"),
-                    utils.get_data_files_path("ansible", "lvm.yml"),
-                    utils.get_data_files_path("ansible",
-                                              "docker-devicemapper.yml"),
-                    utils.get_data_files_path(
-                        "ansible", "kolla-ansible-user.yml"),
-                    utils.get_data_files_path("ansible", "kolla-pip.yml"),
-                    utils.get_data_files_path(
-                        "ansible", "kolla-target-venv.yml"),
+                        "ansible", "overcloud-host-configure.yml"),
                 ],
                 limit="overcloud",
             ),
@@ -1324,6 +1336,60 @@ class TestCase(unittest.TestCase):
         ]
         self.assertEqual(expected_calls, mock_kolla_run.call_args_list)
 
+    @mock.patch.object(commands.KayobeAnsibleMixin,
+                       "run_kayobe_playbooks")
+    @mock.patch.object(commands.KollaAnsibleMixin,
+                       "run_kolla_ansible_overcloud")
+    def test_overcloud_host_configure_wipe_disks(self, mock_kolla_run,
+                                                 mock_run):
+        command = commands.OvercloudHostConfigure(TestApp(), [])
+        parser = command.get_parser("test")
+        parsed_args = parser.parse_args(["--wipe-disks"])
+
+        result = command.run(parsed_args)
+        self.assertEqual(0, result)
+
+        expected_calls = [
+            mock.call(
+                mock.ANY,
+                [utils.get_data_files_path("ansible", "ip-allocation.yml")],
+                limit="overcloud",
+            ),
+            mock.call(
+                mock.ANY,
+                [
+                    utils.get_data_files_path(
+                        "ansible", "overcloud-host-configure.yml"),
+                ],
+                limit="overcloud",
+                extra_vars={"wipe_disks": True},
+            ),
+            mock.call(
+                mock.ANY,
+                [utils.get_data_files_path("ansible", "kolla-ansible.yml")],
+                tags="config",
+                ignore_limit=True,
+            ),
+            mock.call(
+                mock.ANY,
+                [
+                    utils.get_data_files_path("ansible", "docker.yml"),
+                    utils.get_data_files_path(
+                        "ansible", "swift-block-devices.yml"),
+                ],
+                limit="overcloud",
+            ),
+        ]
+        self.assertEqual(expected_calls, mock_run.call_args_list)
+
+        expected_calls = [
+            mock.call(
+                mock.ANY,
+                "bootstrap-servers",
+            ),
+        ]
+        self.assertEqual(expected_calls, mock_kolla_run.call_args_list)
+
     @mock.patch.object(commands.KayobeAnsibleMixin,
                        "run_kayobe_playbooks")
     def test_overcloud_host_command_run(self, mock_run):
@@ -1443,13 +1509,7 @@ class TestCase(unittest.TestCase):
                 mock.ANY,
                 [
                     utils.get_data_files_path(
-                        "ansible", "kayobe-target-venv.yml"),
-                    utils.get_data_files_path(
-                        "ansible", "kolla-target-venv.yml"),
-                    utils.get_data_files_path(
-                        "ansible", "overcloud-docker-sdk-upgrade.yml"),
-                    utils.get_data_files_path(
-                        "ansible", "overcloud-etc-hosts-fixup.yml"),
+                        "ansible", "overcloud-host-upgrade.yml"),
                 ],
                 limit="overcloud",
             ),
diff --git a/releasenotes/notes/host-configure-single-playbook-060e9fd96e0aebb5.yaml b/releasenotes/notes/host-configure-single-playbook-060e9fd96e0aebb5.yaml
new file mode 100644
index 000000000..418ebabee
--- /dev/null
+++ b/releasenotes/notes/host-configure-single-playbook-060e9fd96e0aebb5.yaml
@@ -0,0 +1,9 @@
+---
+features:
+  - |
+    Improves error handling by adding a top-level playbook for the ``kayobe *
+    host configure`` and ``kayobe * host upgrade`` commands. This ensures that
+    if a host fails during a host configuration command, other hosts are able
+    to continue to completion. This is useful at scale, where host failures
+    occur more frequently. See `story 2009854
+    <https://storyboard.openstack.org/#!/story/2009854>`__ for details.