diff --git a/.zuul.d/jobs.yaml b/.zuul.d/jobs.yaml
index b7d97ecc8..38c311894 100644
--- a/.zuul.d/jobs.yaml
+++ b/.zuul.d/jobs.yaml
@@ -99,6 +99,19 @@
         # TODO: set when CentOS 8 Stream repos mirrored.
         # mirror: "http://{{ zuul_site_mirror_fqdn }}/centos"
 
+- job:
+    name: dib-nodepool-functional-openstack-centos-9-stream-src
+    description: |
+      Test building and booting a Centos 9 Stream image with Nodepool and
+      OpenStack.
+    parent: dib-nodepool-functional-src-base
+    vars:
+      nodepool_diskimage:
+        base_element: centos-minimal
+        release: '9-stream'
+        # TODO: set when CentOS 9 Stream repos mirrored.
+        # mirror: "http://{{ zuul_site_mirror_fqdn }}/centos"
+
 - job:
     name: dib-nodepool-functional-openstack-fedora-34-containerfile-src
     description: |
diff --git a/.zuul.d/project.yaml b/.zuul.d/project.yaml
index c132197f9..7745a7cdb 100644
--- a/.zuul.d/project.yaml
+++ b/.zuul.d/project.yaml
@@ -17,6 +17,7 @@
         - dib-nodepool-functional-openstack-centos-7-src
         - dib-nodepool-functional-openstack-centos-8-src
         - dib-nodepool-functional-openstack-centos-8-stream-src
+        - dib-nodepool-functional-openstack-centos-9-stream-src
         - dib-nodepool-functional-openstack-fedora-34-containerfile-src
         - dib-nodepool-functional-openstack-ubuntu-xenial-src
         - dib-nodepool-functional-openstack-ubuntu-bionic-src
@@ -46,6 +47,7 @@
         - dib-nodepool-functional-openstack-centos-7-src
         - dib-nodepool-functional-openstack-centos-8-src
         - dib-nodepool-functional-openstack-centos-8-stream-src
+        - dib-nodepool-functional-openstack-centos-9-stream-src
         - dib-nodepool-functional-openstack-fedora-34-containerfile-src
         - dib-nodepool-functional-openstack-ubuntu-xenial-src
         - dib-nodepool-functional-openstack-ubuntu-bionic-src
diff --git a/contrib/setup-gate-mirrors.sh b/contrib/setup-gate-mirrors.sh
index 5e8f4c9f8..003459db9 100755
--- a/contrib/setup-gate-mirrors.sh
+++ b/contrib/setup-gate-mirrors.sh
@@ -103,5 +103,31 @@ baseurl=$NODEPOOL_CENTOS_MIRROR/\$releasever/extras/\$basearch/os/
 gpgcheck=0
 EOF
 
+# Centos 8-stream Minimal
+CENTOS_MIN_DIR=$BASE_DIR/centos-minimal/yum.repos.d/8-stream
+mkdir -p $CENTOS_MIN_DIR
+
+cat <<EOF > $CENTOS_MIN_DIR/dib-mirror-base.repo
+[base]
+name=CentOS-\$releasever - Base
+baseurl=$NODEPOOL_CENTOS_MIRROR/\$releasever/BaseOS/\$basearch/os/
+gpgcheck=0
+EOF
+
+cat <<EOF > $CENTOS_MIN_DIR/dib-mirror-appstream.repo
+[base]
+name=CentOS-\$releasever - AppStream
+baseurl=$NODEPOOL_CENTOS_MIRROR/\$releasever/AppStream/\$basearch/os/
+gpgcheck=0
+EOF
+
+
+cat <<EOF > $CENTOS_MIN_DIR/dib-mirror-extras.repo
+#additional packages that may be useful
+[extras]
+name=CentOS-\$releasever - Extras
+baseurl=$NODEPOOL_CENTOS_MIRROR/\$releasever/extras/\$basearch/os/
+gpgcheck=0
+EOF
 
 ## apt sources (todo)
diff --git a/diskimage_builder/elements/bootloader/finalise.d/50-bootloader b/diskimage_builder/elements/bootloader/finalise.d/50-bootloader
index 81aa257a3..d042d0edf 100755
--- a/diskimage_builder/elements/bootloader/finalise.d/50-bootloader
+++ b/diskimage_builder/elements/bootloader/finalise.d/50-bootloader
@@ -165,6 +165,12 @@ if grep -qe "^\s*GRUB_ENABLE_BLSCFG=true" /etc/default/grub; then
     USE_GRUBBY=true
 fi
 
+# When building CentOS9 with centos-minimal /etc/default/grub does not exist
+# after grub2-tools installation. However we need CS9 to use grubby.
+if [[ "$DISTRO_NAME" == "centos" ]] && [[ $DIB_RELEASE =~ 9 ]]; then
+    USE_GRUBBY=true
+fi
+
 # Override the root device to the default label, and disable uuid
 # lookup.
 if [ -n "$USE_GRUBBY" ]; then
diff --git a/diskimage_builder/elements/centos-minimal/test-elements/9-stream-build-succeeds/README.rst b/diskimage_builder/elements/centos-minimal/test-elements/9-stream-build-succeeds/README.rst
new file mode 100644
index 000000000..fa1e50dc9
--- /dev/null
+++ b/diskimage_builder/elements/centos-minimal/test-elements/9-stream-build-succeeds/README.rst
@@ -0,0 +1 @@
+Verify we can build a centos-minimal image.
diff --git a/diskimage_builder/elements/centos-minimal/test-elements/9-stream-build-succeeds/element-deps b/diskimage_builder/elements/centos-minimal/test-elements/9-stream-build-succeeds/element-deps
new file mode 100644
index 000000000..5d58b59ff
--- /dev/null
+++ b/diskimage_builder/elements/centos-minimal/test-elements/9-stream-build-succeeds/element-deps
@@ -0,0 +1 @@
+openstack-ci-mirrors
diff --git a/diskimage_builder/elements/centos-minimal/test-elements/9-stream-build-succeeds/environment.d/09-set-distro.bash b/diskimage_builder/elements/centos-minimal/test-elements/9-stream-build-succeeds/environment.d/09-set-distro.bash
new file mode 100644
index 000000000..29a6ae5ea
--- /dev/null
+++ b/diskimage_builder/elements/centos-minimal/test-elements/9-stream-build-succeeds/environment.d/09-set-distro.bash
@@ -0,0 +1 @@
+export DIB_RELEASE='9-stream'
diff --git a/diskimage_builder/elements/centos-minimal/yum.repos.d/8-stream/appstream.repo b/diskimage_builder/elements/centos-minimal/yum.repos.d/8-stream/appstream.repo
index e4213df3e..0c8c2a206 100644
--- a/diskimage_builder/elements/centos-minimal/yum.repos.d/8-stream/appstream.repo
+++ b/diskimage_builder/elements/centos-minimal/yum.repos.d/8-stream/appstream.repo
@@ -1,5 +1,5 @@
 [Stream-AppStream]
 name=CentOS-Stream - AppStream
-mirrorlist=http://mirrorlist.centos.org/?release=8-stream&arch=$basearch&repo=AppStream
+mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=AppStream
 gpgcheck=0
 
diff --git a/diskimage_builder/elements/centos-minimal/yum.repos.d/8-stream/base.repo b/diskimage_builder/elements/centos-minimal/yum.repos.d/8-stream/base.repo
index 6b48d2a90..b8546d9a3 100644
--- a/diskimage_builder/elements/centos-minimal/yum.repos.d/8-stream/base.repo
+++ b/diskimage_builder/elements/centos-minimal/yum.repos.d/8-stream/base.repo
@@ -1,5 +1,5 @@
 [Stream-BaseOS]
 name=CentOS-Stream - Base
-mirrorlist=http://mirrorlist.centos.org/?release=8-stream&arch=$basearch&repo=BaseOS
+mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=BaseOS
 gpgcheck=0
 
diff --git a/diskimage_builder/elements/centos-minimal/yum.repos.d/9-stream/appstream.repo b/diskimage_builder/elements/centos-minimal/yum.repos.d/9-stream/appstream.repo
new file mode 100644
index 000000000..0de9fbaba
--- /dev/null
+++ b/diskimage_builder/elements/centos-minimal/yum.repos.d/9-stream/appstream.repo
@@ -0,0 +1,10 @@
+[appstream]
+name=CentOS Stream 9 - AppStream
+metalink=https://mirrors.centos.org/metalink?repo=centos-appstream-9-stream&arch=$basearch
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
+gpgcheck=0
+repo_gpgcheck=0
+metadata_expire=6h
+countme=1
+enabled=1
+
diff --git a/diskimage_builder/elements/centos-minimal/yum.repos.d/9-stream/base.repo b/diskimage_builder/elements/centos-minimal/yum.repos.d/9-stream/base.repo
new file mode 100644
index 000000000..2f07ec2bb
--- /dev/null
+++ b/diskimage_builder/elements/centos-minimal/yum.repos.d/9-stream/base.repo
@@ -0,0 +1,10 @@
+[baseos]
+name=CentOS Stream 9 - BaseOS
+metalink=https://mirrors.centos.org/metalink?repo=centos-baseos-9-stream&arch=$basearch
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
+gpgcheck=0
+repo_gpgcheck=0
+metadata_expire=6h
+countme=1
+enabled=1
+
diff --git a/diskimage_builder/elements/yum-minimal/package-installs.yaml b/diskimage_builder/elements/yum-minimal/package-installs.yaml
index 1b66417cb..3ed6955ef 100644
--- a/diskimage_builder/elements/yum-minimal/package-installs.yaml
+++ b/diskimage_builder/elements/yum-minimal/package-installs.yaml
@@ -17,4 +17,4 @@ linux-firmware-whence:
 # mirrors the default packages installed in upstream cloud images
 # to facilitate a basic network.
 NetworkManager:
-dhcp-client:
\ No newline at end of file
+dhcp-client:
diff --git a/diskimage_builder/elements/yum-minimal/pkg-map b/diskimage_builder/elements/yum-minimal/pkg-map
index ec3343113..45d8b27e9 100644
--- a/diskimage_builder/elements/yum-minimal/pkg-map
+++ b/diskimage_builder/elements/yum-minimal/pkg-map
@@ -15,6 +15,13 @@
           }
       }
   },
+  "release": {
+      "centos": {
+          "9-stream": {
+              "lsb_release": "ed hostname patch postfix tar time"
+          }
+      }
+  },
 "family": {
     "redhat": {
       "lsb_release": "redhat-lsb-core"
diff --git a/diskimage_builder/elements/yum-minimal/root.d/08-yum-chroot b/diskimage_builder/elements/yum-minimal/root.d/08-yum-chroot
index 3be0fe233..f848cb738 100755
--- a/diskimage_builder/elements/yum-minimal/root.d/08-yum-chroot
+++ b/diskimage_builder/elements/yum-minimal/root.d/08-yum-chroot
@@ -87,6 +87,8 @@ function _install_repos {
     packages+="basesystem filesystem setup "
     if [[ ${DISTRO_NAME} = fedora && ${DIB_RELEASE} -gt 29 ]]; then
         packages+="fedora-release-cloud fedora-release-common "
+    elif [[ ${DISTRO_NAME} == 'centos' && ${DIB_RELEASE} = '9-stream' ]]; then
+        packages+="bash glibc ncurses-libs "
     elif [[ ${DISTRO_NAME} == 'openeuler' ]]; then
         packages+="openEuler-release "
     else
@@ -104,7 +106,7 @@ function _install_repos {
     fi
 
     # CentOS 8.1 split repositories and GPG keys out into subpackages
-    if [[ ${DISTRO_NAME} = centos && ${DIB_RELEASE} > "7" ]]; then
+    if [[ ${DISTRO_NAME} = centos && ${DIB_RELEASE%-stream} -gt 7 ]]; then
         packages+="centos-gpg-keys "
         if [[ "$DIB_RELEASE" =~ (stream) ]]; then
             packages+="centos-stream-release centos-stream-repos "
@@ -141,7 +143,7 @@ function _install_repos {
     local temp_tmp
     temp_tmp=$(mktemp -d)
     TMPDIR=${temp_tmp} ${HOST_YUM_DOWNLOADER} --verbose \
-        --releasever=${DIB_RELEASE/-*/} \
+        --releasever=${DIB_RELEASE} \
         --setopt=reposdir=$repo \
         --setopt=cachedir=$temp_tmp \
         --destdir=$WORKING \
diff --git a/roles/dib-functests/tasks/main.yaml b/roles/dib-functests/tasks/main.yaml
index 44ac97b67..e096706a1 100644
--- a/roles/dib-functests/tasks/main.yaml
+++ b/roles/dib-functests/tasks/main.yaml
@@ -49,6 +49,7 @@
   args:
     executable: /bin/bash
   become: yes
+  when: ansible_os_family == 'Debian'
 
 - name: Run dib functional tests
   shell:
diff --git a/roles/dib-setup-gate-mirrors/tasks/main.yaml b/roles/dib-setup-gate-mirrors/tasks/main.yaml
index 40329909c..c2c876d81 100644
--- a/roles/dib-setup-gate-mirrors/tasks/main.yaml
+++ b/roles/dib-setup-gate-mirrors/tasks/main.yaml
@@ -39,6 +39,22 @@
     - appstream.repo
     - extras.repo
 
+- name: Create centos-minimal 9-stream directory
+  file:
+    path: "{{ dib_gate_mirror_repos }}/centos-minimal/9-stream/yum.repos.d"
+    state: directory
+    mode: 0775
+    recurse: yes
+
+- name: Install centos-minimal 9-stream repo files
+  template:
+    dest: "{{ dib_gate_mirror_repos }}/centos-minimal/9-stream/yum.repos.d/dib-mirror-{{ item }}"
+    mode: 0644
+    src: "centos-minimal/9-stream/{{ item }}.j2"
+  with_items:
+    - base.repo
+    - appstream.repo
+
 - name: Create fedora-minimal directories
   file:
     path: "{{ dib_gate_mirror_repos }}/fedora-minimal/{{ item }}/yum.repos.d"
diff --git a/roles/dib-setup-gate-mirrors/templates/centos-minimal/9-stream/appstream.repo.j2 b/roles/dib-setup-gate-mirrors/templates/centos-minimal/9-stream/appstream.repo.j2
new file mode 100644
index 000000000..9a15115bb
--- /dev/null
+++ b/roles/dib-setup-gate-mirrors/templates/centos-minimal/9-stream/appstream.repo.j2
@@ -0,0 +1,6 @@
+[Stream-AppStream]
+name=CentOS-Stream - AppStream
+# Switch to mirrors once available
+metalink=https://mirrors.centos.org/metalink?repo=centos-appstream-9-stream&arch=$basearch
+gpgcheck=0
+
diff --git a/roles/dib-setup-gate-mirrors/templates/centos-minimal/9-stream/base.repo.j2 b/roles/dib-setup-gate-mirrors/templates/centos-minimal/9-stream/base.repo.j2
new file mode 100644
index 000000000..ec2853ba6
--- /dev/null
+++ b/roles/dib-setup-gate-mirrors/templates/centos-minimal/9-stream/base.repo.j2
@@ -0,0 +1,6 @@
+[Stream-BaseOS]
+name=CentOS-Stream - BaseOS
+# Switch to mirrors once available
+metalink=https://mirrors.centos.org/metalink?repo=centos-baseos-9-stream&arch=$basearch
+gpgcheck=0
+
diff --git a/tests/install_test_deps.sh b/tests/install_test_deps.sh
index 1d7b6f457..ed73af55a 100755
--- a/tests/install_test_deps.sh
+++ b/tests/install_test_deps.sh
@@ -7,6 +7,7 @@ sudo apt-get update || true
 sudo apt-get install -y --force-yes \
         docker.io || \
     sudo yum -y install --enablerepo=epel \
+         yum-utils \
          debootstrap \
          dpkg \
          docker || \