diff --git a/ansible/inventory/group_vars/all/infra-vms b/ansible/inventory/group_vars/all/infra-vms
index c78494ca4b960b6f518641e6145a6faa9c33eaf1..569ab6b73a7a046e3fa7109e08d3da530328f49a 100644
--- a/ansible/inventory/group_vars/all/infra-vms
+++ b/ansible/inventory/group_vars/all/infra-vms
@@ -76,6 +76,12 @@ infra_vm_wait_connection_ssh_extra_args: '-o StrictHostKeyChecking=no'
 # OS family. Needed for config drive generation.
 infra_vm_os_family: "{{ 'RedHat' if os_distribution in ['centos', 'rocky'] else 'Debian' }}"
 
+# Boot firmware. Possible values are 'bios' or 'efi'. Default is 'bios'.
+infra_vm_boot_firmware: "bios"
+
+# Machine type. Libvirt default configuration is used.
+infra_vm_machine:
+
 ###############################################################################
 # Infrastructure VM node configuration.
 
diff --git a/ansible/inventory/group_vars/all/seed-vm b/ansible/inventory/group_vars/all/seed-vm
index 8f63d0e8305ee6180cd2777de90033c895351d78..953d580fdeb06d1fce28040a3edc9651ce844f07 100644
--- a/ansible/inventory/group_vars/all/seed-vm
+++ b/ansible/inventory/group_vars/all/seed-vm
@@ -65,3 +65,9 @@ seed_vm_data_format: qcow2
 
 # List of network interfaces to attach to the seed VM.
 seed_vm_interfaces: "{{ network_interfaces | sort | map('net_libvirt_vm_network') | list }}"
+
+# Boot firmware. Possible values are 'bios' or 'efi'. Default is 'bios'.
+seed_vm_boot_firmware: "bios"
+
+# Machine type. Libvirt default configuration is used.
+seed_vm_machine:
diff --git a/ansible/roles/infra-vms/tasks/deploy.yml b/ansible/roles/infra-vms/tasks/deploy.yml
index 214d0be3fddeea31ad2ca58ad950d290eda60894..2000bfdc68c5f52f05697ff2ecccad197aac5da8 100644
--- a/ansible/roles/infra-vms/tasks/deploy.yml
+++ b/ansible/roles/infra-vms/tasks/deploy.yml
@@ -85,6 +85,8 @@
     libvirt_vm_image_cache_path: "{{ image_cache_path }}"
     libvirt_vms:
       - name: "{{ vm_name }}"
+        boot_firmware: "{{ vm_hostvars.infra_vm_boot_firmware | default }}"
+        machine: "{{ vm_hostvars.infra_vm_machine | default }}"
         memory_mb: "{{ vm_hostvars.infra_vm_memory_mb }}"
         vcpus: "{{ vm_hostvars.infra_vm_vcpus }}"
         volumes: "{{ vm_hostvars.infra_vm_volumes + [vm_configdrive_volume] }}"
diff --git a/ansible/roles/infra-vms/tasks/destroy.yml b/ansible/roles/infra-vms/tasks/destroy.yml
index a621e4e30f272624bed9820adcc054af8b34e5e7..c45b996c76de6714e7be2678387f395faf5da209 100644
--- a/ansible/roles/infra-vms/tasks/destroy.yml
+++ b/ansible/roles/infra-vms/tasks/destroy.yml
@@ -9,6 +9,7 @@
       pool: "{{ hostvars[vm_hostvars.infra_vm_hypervisor].infra_vm_pool }}"
     libvirt_vms:
       - name: "{{ vm_name }}"
+        boot_firmware: "{{ vm_hostvars.infra_vm_boot_firmware | default }}"
         memory_mb: "{{ vm_hostvars.infra_vm_memory_mb }}"
         vcpus: "{{ vm_hostvars.infra_vm_vcpus }}"
         volumes: "{{ vm_hostvars.infra_vm_volumes + [infra_vm_configdrive_volume] }}"
diff --git a/ansible/seed-vm-deprovision.yml b/ansible/seed-vm-deprovision.yml
index 1ed2179e9927049c4909e88cf2b5568e534198c9..9edd634b7d85bb0f68d9b57301aa1d3b9ebfdff6 100644
--- a/ansible/seed-vm-deprovision.yml
+++ b/ansible/seed-vm-deprovision.yml
@@ -8,9 +8,14 @@
       seed_vm_configdrive_volume:
         name: "{{ hostvars[seed_host].seed_vm_name }}-configdrive"
         pool: "{{ hostvars[seed_host].seed_vm_pool }}"
-      libvirt_vm_name: "{{ hostvars[seed_host].seed_vm_name }}"
-      libvirt_vm_memory_mb: "{{ hostvars[seed_host].seed_vm_memory_mb }}"
-      libvirt_vm_vcpus: "{{ hostvars[seed_host].seed_vm_vcpus }}"
-      libvirt_vm_volumes: "{{ hostvars[seed_host].seed_vm_volumes + [seed_vm_configdrive_volume] }}"
-      libvirt_vm_state: "absent"
+      libvirt_vms:
+        - name: "{{ hostvars[seed_host].seed_vm_name }}"
+          boot_firmware: "{{ hostvars[seed_host].seed_vm_boot_firmware | default }}"
+          machine: "{{ hostvars[seed_host].seed_vm_machine | default }}"
+          memory_mb: "{{ hostvars[seed_host].seed_vm_memory_mb }}"
+          vcpus: "{{ hostvars[seed_host].seed_vm_vcpus }}"
+          volumes: "{{ hostvars[seed_host].seed_vm_volumes + [seed_vm_configdrive_volume] }}"
+          interfaces: "{{ hostvars[seed_host].seed_vm_interfaces }}"
+          console_log_enabled: true
+          state: absent
       become: True
diff --git a/ansible/seed-vm-provision.yml b/ansible/seed-vm-provision.yml
index 41f97996cc51443c01f4bac9b9e7db84da66232a..8be7364ef457ea59c400cbae3e036241a0c019ea 100644
--- a/ansible/seed-vm-provision.yml
+++ b/ansible/seed-vm-provision.yml
@@ -115,12 +115,13 @@
       libvirt_vm_image_cache_path: "{{ image_cache_path }}"
       libvirt_vms:
         - name: "{{ hostvars[seed_host].seed_vm_name }}"
+          boot_firmware: "{{ hostvars[seed_host].seed_vm_boot_firmware | default }}"
+          machine: "{{ hostvars[seed_host].seed_vm_machine | default }}"
           memory_mb: "{{ hostvars[seed_host].seed_vm_memory_mb }}"
           vcpus: "{{ hostvars[seed_host].seed_vm_vcpus }}"
           volumes: "{{ hostvars[seed_host].seed_vm_volumes + [seed_vm_configdrive_volume] }}"
           interfaces: "{{ hostvars[seed_host].seed_vm_interfaces }}"
           console_log_enabled: true
-
   tasks:
     - name: Wait for SSH access to the seed VM
       local_action:
diff --git a/etc/kayobe/infra-vms.yml b/etc/kayobe/infra-vms.yml
index a8f1fd9b21694ec02292d4380440fe7577932999..ba75c641c10aa2101aa797003171831cb8eed5dd 100644
--- a/etc/kayobe/infra-vms.yml
+++ b/etc/kayobe/infra-vms.yml
@@ -58,6 +58,12 @@
 # OS family. Needed for config drive generation.
 #infra_vm_os_family:
 
+# Boot firmware. Possible values are 'bios' or 'efi'. Default is 'bios'.
+#infra_vm_boot_firmware:
+
+# Machine type. Libvirt default configuration is used.
+#infra_vm_machine:
+
 ###############################################################################
 # Infrastructure VM node configuration.
 
diff --git a/etc/kayobe/seed-vm.yml b/etc/kayobe/seed-vm.yml
index f10fbaa193eedfd47317496e4b17dd1ed5651058..5fdcc1690236706554d03f09116a682310f12c6e 100644
--- a/etc/kayobe/seed-vm.yml
+++ b/etc/kayobe/seed-vm.yml
@@ -61,6 +61,12 @@
 #
 #seed_vm_interfaces:
 
+# Boot firmware. Possible values are 'bios' or 'efi'. Default is 'bios'.
+#seed_vm_boot_firmware:
+
+# Machine type. Libvirt default configuration is used.
+#seed_vm_machine:
+
 ###############################################################################
 # Dummy variable to allow Ansible to accept this file.
 workaround_ansible_issue_8743: yes
diff --git a/playbooks/kayobe-seed-vm-base/overrides.yml.j2 b/playbooks/kayobe-seed-vm-base/overrides.yml.j2
index 57706d8097ba886e08b41ca4a46cf70b188cae57..c7284ce5eac67e0d449d7197c5ad9a4a4a61ff4a 100644
--- a/playbooks/kayobe-seed-vm-base/overrides.yml.j2
+++ b/playbooks/kayobe-seed-vm-base/overrides.yml.j2
@@ -33,6 +33,13 @@ seed_vm_memory_mb: "{{ 1 * 1024 }}"
 seed_bootstrap_user: cirros
 seed_vm_root_image: /opt/cache/files/cirros-0.5.3-x86_64-disk.img
 
+{% if seed_vm_boot_firmware is defined %}
+seed_vm_boot_firmware: "{{ seed_vm_boot_firmware }}"
+{% endif %}
+{% if seed_vm_machine is defined %}
+seed_vm_machine: "{{ seed_vm_machine }}"
+{% endif %}
+
 # Cirros doesn't load cdom drivers by default.
 seed_vm_configdrive_device: disk
 
diff --git a/releasenotes/notes/seed-infra-vm-boot-fw-machine-096251c45653f98c.yaml b/releasenotes/notes/seed-infra-vm-boot-fw-machine-096251c45653f98c.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..db4c48a5d4cd17c97ddd14655ecc483ff6f71c3e
--- /dev/null
+++ b/releasenotes/notes/seed-infra-vm-boot-fw-machine-096251c45653f98c.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    Adds support for specifying ``boot_firmware`` and ``machine`` variables to
+    seed and infra VMs. This can be used to launch VMs in UEFI boot mode with
+    Q35 machine type.
+  - |
+    Bumps stackhpc.libvirt-vm Ansible role to ``v1.16.1``.
diff --git a/requirements.yml b/requirements.yml
index d061f3536536c7b81578bdde74cb08e18a4a48c3..641c29059fa48f2f641ef5302a537c03c05f768b 100644
--- a/requirements.yml
+++ b/requirements.yml
@@ -40,8 +40,8 @@ roles:
     version: 1.0.0
   - src: stackhpc.libvirt-host
     version: v1.12.1
-  - src: stackhpc.libvirt-vm
-    version: v1.14.2
+  - name: stackhpc.libvirt-vm
+    version: v1.16.1
   - src: stackhpc.luks
     version: 0.4.2
   - src: stackhpc.os-ironic-state
diff --git a/zuul.d/jobs.yaml b/zuul.d/jobs.yaml
index d6b3b6c64c3eca2eb6bffb24b7485c051637c7c7..743c9487085d00913d5492135e880ffe82f3ffc8 100644
--- a/zuul.d/jobs.yaml
+++ b/zuul.d/jobs.yaml
@@ -337,6 +337,31 @@
     parent: kayobe-seed-vm-base
     nodeset: kayobe-ubuntu-jammy
 
+- job:
+    name: kayobe-seed-vm-efi-base
+    parent: kayobe-seed-vm-base
+    description: |
+      Base job for testing seed VM provisioning with EFI and q35
+    vars:
+      seed_vm_boot_firmware: efi
+      seed_vm_machine: q35
+
+- job:
+    name: kayobe-seed-vm-centos9s-efi
+    parent: kayobe-seed-vm-efi-base
+    nodeset: kayobe-centos9s
+    voting: false
+
+- job:
+    name: kayobe-seed-vm-rocky9-efi
+    parent: kayobe-seed-vm-efi-base
+    nodeset: kayobe-rocky9
+
+- job:
+    name: kayobe-seed-vm-ubuntu-jammy-efi
+    parent: kayobe-seed-vm-efi-base
+    nodeset: kayobe-ubuntu-jammy
+
 - job:
     name: kayobe-infra-vm-base
     parent: kayobe-base
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index a09d68e5f480b837f6ed4d9e68a289125b3c88d8..132ac132e1008653279b7f70feab3ad22d380fbe 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -26,7 +26,9 @@
         - kayobe-seed-upgrade-rocky9
         - kayobe-seed-upgrade-ubuntu-jammy
         - kayobe-seed-vm-rocky9
+        - kayobe-seed-vm-rocky9-efi
         - kayobe-seed-vm-ubuntu-jammy
+        - kayobe-seed-vm-ubuntu-jammy-efi
         - kayobe-infra-vm-rocky9
         - kayobe-infra-vm-ubuntu-jammy
     gate: