diff --git a/ansible/group_vars/all/bifrost b/ansible/group_vars/all/bifrost
index 81224ed1e424d7a8e8e185b47f867441f85aad86..d26c9c8685d485d732e1d6b4eac9c3adccb1b02a 100644
--- a/ansible/group_vars/all/bifrost
+++ b/ansible/group_vars/all/bifrost
@@ -18,15 +18,18 @@ kolla_bifrost_firewalld_internal_zone: trusted
 ###############################################################################
 # Diskimage-builder configuration.
 
-# DIB base OS element.
-kolla_bifrost_dib_os_element: "centos"
+# DIB base OS element. Default is {{ os_distribution }}.
+kolla_bifrost_dib_os_element: "{{ os_distribution }}"
 
-# DIB image OS release.
-kolla_bifrost_dib_os_release: "8"
+# DIB image OS release. Default is "focal" when os_distribution is "ubuntu", or
+# "8" otherwise.
+kolla_bifrost_dib_os_release: "{{ 'focal' if os_distribution == 'ubuntu' else '8' }}"
 
-# List of default DIB elements.
+# List of default DIB elements. Default is ["disable-selinux",
+# "enable-serial-console", "vm"] when os_distribution is "centos", or
+# ["enable-serial-console", "vm"] otherwise.
 kolla_bifrost_dib_elements_default:
-  - "disable-selinux"
+  - "{% if os_distribution == 'centos' %}disable-selinux{% endif %}"
   - "enable-serial-console"
   - "vm"
 
@@ -34,7 +37,7 @@ kolla_bifrost_dib_elements_default:
 kolla_bifrost_dib_elements_extra: []
 
 # List of all DIB elements.
-kolla_bifrost_dib_elements: "{{ kolla_bifrost_dib_elements_default + kolla_bifrost_dib_elements_extra }}"
+kolla_bifrost_dib_elements: "{{ kolla_bifrost_dib_elements_default | select | list + kolla_bifrost_dib_elements_extra }}"
 
 # DIB init element.
 kolla_bifrost_dib_init_element: "cloud-init-datasources"
diff --git a/ansible/group_vars/all/compute b/ansible/group_vars/all/compute
index eb5f7bd905db18190a505fc029bd19f378c8a360..61bbe91f1bdc4c9f38af95d5fbc117b13e86c715 100644
--- a/ansible/group_vars/all/compute
+++ b/ansible/group_vars/all/compute
@@ -3,8 +3,8 @@
 # Compute node configuration.
 
 # User with which to access the computes via SSH during bootstrap, in order
-# to setup the Kayobe user account.
-compute_bootstrap_user: "{{ lookup('env', 'USER') }}"
+# to setup the Kayobe user account. Default is {{ os_distribution }}.
+compute_bootstrap_user: "{{ os_distribution }}"
 
 ###############################################################################
 # Compute network interface configuration.
diff --git a/ansible/group_vars/all/controllers b/ansible/group_vars/all/controllers
index e820bcf1136efadccd160106ff88a36b2a9fec54..0c09024fad5f442a4ed269e80dc71ce024788871 100644
--- a/ansible/group_vars/all/controllers
+++ b/ansible/group_vars/all/controllers
@@ -3,8 +3,8 @@
 # Controller node configuration.
 
 # User with which to access the controllers via SSH during bootstrap, in order
-# to setup the Kayobe user account.
-controller_bootstrap_user: "{{ lookup('env', 'USER') }}"
+# to setup the Kayobe user account. Default is {{ os_distribution }}.
+controller_bootstrap_user: "{{ os_distribution }}"
 
 ###############################################################################
 # Controller network interface configuration.
diff --git a/ansible/group_vars/all/globals b/ansible/group_vars/all/globals
index e5dc329c4685c3053cb28c13e26f0c02890bb9ea..9d9d6e3148ae2225c92add67fdc6d05cd1292408 100644
--- a/ansible/group_vars/all/globals
+++ b/ansible/group_vars/all/globals
@@ -40,3 +40,10 @@ virtualenv_path: "{{ base_path ~ '/venvs' }}"
 # User with which to access remote hosts. This user will be created if it does
 # not exist.
 kayobe_ansible_user: "stack"
+
+###############################################################################
+# OS distribution.
+
+# OS distribution name. Valid options are "centos", "ubuntu". Default is
+# "centos".
+os_distribution: "centos"
diff --git a/ansible/group_vars/all/kolla b/ansible/group_vars/all/kolla
index 3c98b95f5f9565ee2cab98c23ce253e9b32bc52b..33aef2c6959ffbccd32c0216c586b4529cd4e62e 100644
--- a/ansible/group_vars/all/kolla
+++ b/ansible/group_vars/all/kolla
@@ -52,8 +52,9 @@ kolla_node_custom_config_path: "{{ kolla_config_path }}/config"
 ###############################################################################
 # Kolla configuration.
 
-# Kolla base container image distribution.
-kolla_base_distro: "centos"
+# Kolla base container image distribution. Options are "centos", "debian",
+# "ubuntu". Default is {{ os_distribution }}.
+kolla_base_distro: "{{ os_distribution }}"
 
 # Kolla container image type: binary or source.
 kolla_install_type: "binary"
diff --git a/ansible/group_vars/all/seed b/ansible/group_vars/all/seed
index 620dedce1950a3fe33b9a047910763ff6774e262..decdd2a52a6ae3752bde4f805558e71e969df608 100644
--- a/ansible/group_vars/all/seed
+++ b/ansible/group_vars/all/seed
@@ -3,8 +3,8 @@
 # Seed node configuration.
 
 # User with which to access the seed via SSH during bootstrap, in order to
-# setup the Kayobe user account.
-seed_bootstrap_user: "{{ lookup('env', 'USER') }}"
+# setup the Kayobe user account. Default is {{ os_distribution }}.
+seed_bootstrap_user: "{{ os_distribution }}"
 
 ###############################################################################
 # Seed network interface configuration.
diff --git a/ansible/group_vars/all/seed-hypervisor b/ansible/group_vars/all/seed-hypervisor
index 2826b9577a96a0095205d0d524e5ee32200cf8eb..9ee93d1187a27a6b2cb8e9b8f7479eb0000d97f1 100644
--- a/ansible/group_vars/all/seed-hypervisor
+++ b/ansible/group_vars/all/seed-hypervisor
@@ -3,8 +3,8 @@
 # Seed hypervisor node configuration.
 
 # User with which to access the seed hypervisor via SSH during bootstrap, in
-# order to setup the Kayobe user account.
-seed_hypervisor_bootstrap_user: "{{ lookup('env', 'USER') }}"
+# order to setup the Kayobe user account. Default is {{ os_distribution }}.
+seed_hypervisor_bootstrap_user: "{{ os_distribution }}"
 
 ###############################################################################
 # Seed hypervisor network interface configuration.
diff --git a/ansible/group_vars/all/seed-vm b/ansible/group_vars/all/seed-vm
index 5af51c369c3412f50de3d9ce89b138535fd9f20a..4dcda8285f1fec8b5ae8af24236ec4bf2b3418b6 100644
--- a/ansible/group_vars/all/seed-vm
+++ b/ansible/group_vars/all/seed-vm
@@ -40,8 +40,17 @@ seed_vm_root_capacity: 50G
 # Format of the seed VM root volume.
 seed_vm_root_format: qcow2
 
-# Base image for the seed VM root volume.
-seed_vm_root_image: "https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-20210210.0.x86_64.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
+# "https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-20210210.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
+  {%- else -%}
+  https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-20210210.0.x86_64.qcow2
+  {%- endif %}
 
 # Capacity of the seed VM data volume.
 seed_vm_data_capacity: 100G
diff --git a/ansible/group_vars/all/storage b/ansible/group_vars/all/storage
index bc21871403b39e37cf4180c64ebdbcfcaab8e77e..b474e30910ceb24796c57121a3021279a5d7b6bc 100644
--- a/ansible/group_vars/all/storage
+++ b/ansible/group_vars/all/storage
@@ -3,8 +3,8 @@
 # Storage node configuration.
 
 # User with which to access the storages via SSH during bootstrap, in order
-# to setup the Kayobe user account.
-storage_bootstrap_user: "{{ lookup('env', 'USER') }}"
+# to setup the Kayobe user account. Default is {{ os_distribution }}.
+storage_bootstrap_user: "{{ os_distribution }}"
 
 ###############################################################################
 # Storage network interface configuration.
diff --git a/etc/kayobe/bifrost.yml b/etc/kayobe/bifrost.yml
index bd7e97e1da8b6ac7c6ada50cc71108b6ad043c18..8d20ebebe38cd3560860c9e53917d16b6813585b 100644
--- a/etc/kayobe/bifrost.yml
+++ b/etc/kayobe/bifrost.yml
@@ -18,14 +18,16 @@
 ###############################################################################
 # Diskimage-builder configuration.
 
-# DIB base OS element. Default is "centos".
+# DIB base OS element. Default is {{ os_distribution }}.
 #kolla_bifrost_dib_os_element:
 
-# DIB image OS release. Default is "8".
+# DIB image OS release. Default is "focal" when os_distribution is "ubuntu", or
+# "8" otherwise.
 #kolla_bifrost_dib_os_release:
 
 # List of default DIB elements. Default is ["disable-selinux",
-# "enable-serial-console", "vm"].
+# "enable-serial-console", "vm"] when os_distribution is "centos", or
+# ["enable-serial-console", "vm"] otherwise.
 #kolla_bifrost_dib_elements_default:
 
 # List of additional DIB elements. Default is none.
diff --git a/etc/kayobe/compute.yml b/etc/kayobe/compute.yml
index ab47953fcf09bf7eab06ea38fac3cf94f71952d1..59a68fa781f5d7697f30353a627130b341ac8306 100644
--- a/etc/kayobe/compute.yml
+++ b/etc/kayobe/compute.yml
@@ -3,7 +3,7 @@
 # Compute node configuration.
 
 # User with which to access the computes via SSH during bootstrap, in order
-# to setup the Kayobe user account.
+# to setup the Kayobe user account. Default is {{ os_distribution }}.
 #compute_bootstrap_user:
 
 ###############################################################################
diff --git a/etc/kayobe/controllers.yml b/etc/kayobe/controllers.yml
index 2fbe85b665d3f14c990ab3fed7547e9eed7dd2ad..6a4e45eeb588e89e44df1a3e46c6adffa7be3dde 100644
--- a/etc/kayobe/controllers.yml
+++ b/etc/kayobe/controllers.yml
@@ -3,7 +3,7 @@
 # Controller node configuration.
 
 # User with which to access the controllers via SSH during bootstrap, in order
-# to setup the Kayobe user account.
+# to setup the Kayobe user account. Default is {{ os_distribution }}.
 #controller_bootstrap_user:
 
 ###############################################################################
diff --git a/etc/kayobe/globals.yml b/etc/kayobe/globals.yml
index bfe9b8a3f9ccd6da35e6cca77179fa8b337d3cd5..64290d926bfc0b7ba974d898e6c7de1bdd7e781b 100644
--- a/etc/kayobe/globals.yml
+++ b/etc/kayobe/globals.yml
@@ -42,6 +42,13 @@
 # not exist.
 #kayobe_ansible_user:
 
+###############################################################################
+# OS distribution.
+
+# OS distribution name. Valid options are "centos", "ubuntu". Default is
+# "centos".
+#os_distribution:
+
 ###############################################################################
 # Dummy variable to allow Ansible to accept this file.
 workaround_ansible_issue_8743: yes
diff --git a/etc/kayobe/kolla.yml b/etc/kayobe/kolla.yml
index fa5c4cb400427e5430cadd0ed1f2a0a1cfa7065e..a08be42144862c7c6f6ccd18602e833ba40f0f5a 100644
--- a/etc/kayobe/kolla.yml
+++ b/etc/kayobe/kolla.yml
@@ -61,7 +61,8 @@
 ###############################################################################
 # Kolla configuration.
 
-# Kolla base container image distribution. Default is 'centos'.
+# Kolla base container image distribution. Options are "centos", "debian",
+# "ubuntu". Default is {{ os_distribution }}.
 #kolla_base_distro:
 
 # Kolla container image type: binary or source. Default is 'binary'.
diff --git a/etc/kayobe/seed-hypervisor.yml b/etc/kayobe/seed-hypervisor.yml
index 1ef898893097624145c71ebec6360f72202555fe..b14c82344f87893f77731f4c93cb8cc8e370eddc 100644
--- a/etc/kayobe/seed-hypervisor.yml
+++ b/etc/kayobe/seed-hypervisor.yml
@@ -3,7 +3,7 @@
 # Seed hypervisor node configuration.
 
 # User with which to access the seed hypervisor via SSH during bootstrap, in
-# order to setup the Kayobe user account.
+# order to setup the Kayobe user account. Default is {{ os_distribution }}.
 #seed_hypervisor_bootstrap_user:
 
 ###############################################################################
diff --git a/etc/kayobe/seed-vm.yml b/etc/kayobe/seed-vm.yml
index 0c4f898875a080b5f962dbae34836bfdb874d5aa..9ad7a74388d8c1acb7b7b1c215aa1fca4bf3af7e 100644
--- a/etc/kayobe/seed-vm.yml
+++ b/etc/kayobe/seed-vm.yml
@@ -24,7 +24,10 @@
 #seed_vm_root_format:
 
 # 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
 # "https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-20210210.0.x86_64.qcow2"
+# otherwise.
 #seed_vm_root_image:
 
 # Capacity of the seed VM data volume.
diff --git a/etc/kayobe/seed.yml b/etc/kayobe/seed.yml
index 88efbcc7b4cf5871821bfeec568efee8dc6ae59f..35f2aadaaea899f68a3b4fc5ec5551c4081c6b4d 100644
--- a/etc/kayobe/seed.yml
+++ b/etc/kayobe/seed.yml
@@ -3,7 +3,7 @@
 # Seed node configuration.
 
 # User with which to access the seed via SSH during bootstrap, in order to
-# setup the Kayobe user account.
+# setup the Kayobe user account. Default is {{ os_distribution }}.
 #seed_bootstrap_user:
 
 ###############################################################################
diff --git a/etc/kayobe/storage.yml b/etc/kayobe/storage.yml
index c8ee66f6293d4cfeaa4a79232c5f712fbf63a7b7..47f63dbaa832affc7e1991120ce3e6430814fa11 100644
--- a/etc/kayobe/storage.yml
+++ b/etc/kayobe/storage.yml
@@ -3,7 +3,7 @@
 # Storage node configuration.
 
 # User with which to access the storages via SSH during bootstrap, in order
-# to setup the Kayobe user account.
+# to setup the Kayobe user account. Default is {{ os_distribution }}.
 #storage_bootstrap_user:
 
 ###############################################################################
diff --git a/releasenotes/notes/os-distribution-69445eb19a611d43.yaml b/releasenotes/notes/os-distribution-69445eb19a611d43.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..69c1f88161bbc594affa2e3190fa7d8a6ab580a1
--- /dev/null
+++ b/releasenotes/notes/os-distribution-69445eb19a611d43.yaml
@@ -0,0 +1,14 @@
+---
+features:
+  - |
+    Adds an ``os_distribution`` variable in ``etc/kayobe/globals.yml``, with a
+    default value of ``centos``. The variable can also be set to ``ubuntu``,
+    and sets sensible default values for other variables.
+upgrade:
+  - |
+    Modifies the default value of ``controller_bootstrap_user``,
+    ``compute_bootstrap_user``, ``seed_bootstrap_user``,
+    ``seed_hypervisor_bootstrap_user`` and ``storage_bootstrap_user`` from
+    using the ``$USER`` environment variable of the Ansible control host to
+    ``os_distribution``. This provides a more predictable default that does not
+    depend on the Ansible execution environment.