diff --git a/.ci/linux-build.sh b/.ci/linux-build.sh
index 7dfb5c3171..c843dda80c 100755
--- a/.ci/linux-build.sh
+++ b/.ci/linux-build.sh
@@ -3,15 +3,21 @@
 set -o errexit
 set -x
 
+. ./.ci/util.sh
+
 ARCH=${ARCH:-"x86_64"}
 COMMON_CFLAGS=""
 OVN_CFLAGS=""
 OPTS="$OPTS --enable-Werror"
 JOBS=${JOBS:-"-j4"}
+OVS_USE_STABLE=${OVS_USE_STABLE:false}
 
 function configure_ovs()
 {
     pushd ovs
+    if [ "$OVS_USE_STABLE" = "true" ]; then
+        checkout_latest_stable_branch
+    fi
     ./boot.sh && ./configure CFLAGS="${COMMON_CFLAGS}" $* || \
     { cat config.log; exit 1; }
     make $JOBS || { cat config.log; exit 1; }
diff --git a/.ci/osx-build.sh b/.ci/osx-build.sh
index 4b78b66dd1..03ffb9287b 100755
--- a/.ci/osx-build.sh
+++ b/.ci/osx-build.sh
@@ -1,13 +1,20 @@
 #!/bin/bash
 
 set -o errexit
+set -x
+
+. ./.ci/util.sh
 
 CFLAGS="-Werror $CFLAGS"
 EXTRA_OPTS=""
+OVS_USE_STABLE=${OVS_USE_STABLE:false}
 
 function configure_ovs()
 {
     pushd ovs
+    if [ "$OVS_USE_STABLE" = "true" ]; then
+        checkout_latest_stable_branch
+    fi
     ./boot.sh && ./configure $*
     make -j4 || { cat config.log; exit 1; }
     popd
diff --git a/.ci/util.sh b/.ci/util.sh
new file mode 100644
index 0000000000..952371dd68
--- /dev/null
+++ b/.ci/util.sh
@@ -0,0 +1,9 @@
+# Tries to guess the latest stable branch in a git repo and checks it out.
+# Assumes the CWD is inside a clone of the repo.  It also assumes stable
+# branch names follow the "branch-x.y" convention.
+function checkout_latest_stable_branch()
+{
+    local branch=$(git branch -a -l '*branch-*' | \
+        sed 's/remotes\/origin\///' | sort -V | tail -1)
+    git checkout $branch
+}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 90dc8a6f19..82f916a997 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -19,13 +19,14 @@ jobs:
         libssl-dev llvm-dev libelf-dev libnuma-dev libpcap-dev  \
         selinux-policy-dev ncat python3-scapy isc-dhcp-server
       m32_dependecies: gcc-multilib
-      ARCH:        ${{ matrix.cfg.arch }}
-      CC:          ${{ matrix.cfg.compiler }}
-      LIBS:        ${{ matrix.cfg.libs }}
-      OPTS:        ${{ matrix.cfg.opts }}
-      TESTSUITE:   ${{ matrix.cfg.testsuite }}
-      TEST_RANGE:  ${{ matrix.cfg.test_range }}
-      SANITIZERS:  ${{ matrix.cfg.sanitizers }}
+      ARCH:           ${{ matrix.cfg.arch }}
+      CC:             ${{ matrix.cfg.compiler }}
+      LIBS:           ${{ matrix.cfg.libs }}
+      OPTS:           ${{ matrix.cfg.opts }}
+      TESTSUITE:      ${{ matrix.cfg.testsuite }}
+      TEST_RANGE:     ${{ matrix.cfg.test_range }}
+      SANITIZERS:     ${{ matrix.cfg.sanitizers }}
+      OVS_USE_STABLE: ${{ github.event_name == 'schedule'}}
 
     name: linux ${{ join(matrix.cfg.*, ' ') }}
     runs-on: ubuntu-20.04
@@ -70,15 +71,15 @@ jobs:
       if: github.event_name == 'schedule'
       uses: actions/checkout@v3
 
-    # Weekly runs test using OVS master instead of the
-    # submodule.
-    - name: checkout OVS master
+    # Weekly runs test using the tip of the most recent stable OVS branch
+    # instead of the submodule (OVS_USE_STABLE gets set to "true").
+    - name: checkout OVS
       if: github.event_name == 'schedule'
       uses: actions/checkout@v3
       with:
         repository: 'openvswitch/ovs'
+        fetch-depth: 0
         path: 'ovs'
-        ref: 'master'
 
     - name: update APT cache
       run:  sudo apt update
@@ -137,8 +138,9 @@ jobs:
 
   build-osx:
     env:
-      CC:    clang
-      OPTS:  --disable-ssl
+      CC:             clang
+      OPTS:           --disable-ssl
+      OVS_USE_STABLE: ${{ github.event_name == 'schedule'}}
 
     name:    osx clang --disable-ssl
     runs-on: macos-latest
@@ -156,15 +158,15 @@ jobs:
     - name: checkout without submodule
       if: github.event_name == 'schedule'
       uses: actions/checkout@v3
-    # Weekly runs test using OVS master instead of the
-    # submodule.
-    - name: checkout OVS master
+    # Weekly runs test using the tip of the most recent stable OVS branch
+    # instead of the submodule (OVS_USE_STABLE gets set to "true")
+    - name: checkout OVS
       if: github.event_name == 'schedule'
       uses: actions/checkout@v3
       with:
         repository: 'openvswitch/ovs'
+        fetch-depth: 0
         path: 'ovs'
-        ref: 'master'
     - name: install dependencies
       run:  brew install automake libtool
     - name: update PATH
diff --git a/Makefile.am b/Makefile.am
index 8c60d4a719..2d650fa1ef 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -93,6 +93,7 @@ EXTRA_DIST = \
 	.ci/ovn-kubernetes/Dockerfile \
 	.ci/ovn-kubernetes/prepare.sh \
 	.ci/ovn-kubernetes/custom.patch \
+	.ci/util.sh \
 	.github/workflows/test.yml \
 	.github/workflows/ovn-kubernetes.yml \
 	boot.sh \
