diff --git a/.gitlab/ci/build-images.gitlab-ci.yml b/.gitlab/ci/build-images.gitlab-ci.yml
index d4a9582ded3bb61b60ddab333f2749d261f057bc..cde1e2b75dbd92d0a4b2a3e9ac43e11ac424c6a3 100644
--- a/.gitlab/ci/build-images.gitlab-ci.yml
+++ b/.gitlab/ci/build-images.gitlab-ci.yml
@@ -29,6 +29,21 @@ build-qa-image as-if-foss:
     - .as-if-foss
     - .build-images:rules:build-qa-image-as-if-foss
 
+retag-gdk-image:
+  extends:
+    - .base-image-build
+    - .build-images:rules:retag-gdk-image
+  tags:
+    - docker
+  stage: build-images
+  needs: []
+  script:
+    - |
+      image="registry.gitlab.com/gitlab-org/gitlab-development-kit/asdf-bootstrapped-verify/main"
+      tag=$(awk '/ARG GDK_SHA=/ {print $2}' qa/gdk/Dockerfile.gdk | sed 's/.*=//g')
+      skopeo login -u $RETAG_GDK_IMAGE_TOKEN_NAME -p $RETAG_GDK_IMAGE_TOKEN $CI_REGISTRY
+      skopeo copy docker://${image}:${tag} docker://${image}:stable-${tag}
+
 build-gdk-image:
   extends:
     - .base-image-build-buildx
@@ -36,7 +51,9 @@ build-gdk-image:
   tags:
     - saas-linux-xlarge-amd64
   stage: build-images
-  needs: []
+  needs:
+    - job: retag-gdk-image
+      optional: true
   script:
     - run_timed_command "scripts/build_gdk_image"
 
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index d8cc67a966a21209499dce5b164014a33e4d35eb..87a02aac70c456ebb29c95dc732ab8ff5372be1d 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -906,6 +906,16 @@
       variables:
         ARCH: amd64,arm64
 
+# When new minor release tag is pushed, re-tag gdk image with pattern ignored by registry cleanup
+.build-images:rules:retag-gdk-image:
+  rules:
+    - !reference [".qa:rules:package-and-test-never-run", rules]
+    - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.0-ee$/ && $CI_PIPELINE_SOURCE == "push"'
+    # In case gdk base tag is updated via backport mr, make sure we retag it with stable prefix as well
+    - if: '$CI_MERGE_REQUEST_IID && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME =~ /^[\d-]+-stable-ee$/'
+      changes:
+        - qa/gdk/Dockerfile.gdk
+
 # We use a multi-stage image to:
 # - (re)build the first stage in master pipelines (including scheduled pipelines), and
 # - build the final stage in code-change pipelines (including MRs), and scheduled pipelines
diff --git a/ee/spec/services/groups/compliance_report_csv_service_spec.rb b/ee/spec/services/groups/compliance_report_csv_service_spec.rb
index 329024ffadb65d7c5e1750f65d21316e417d8c29..962494cd8af9ed3e31b5ee46ddc276b168852b64 100644
--- a/ee/spec/services/groups/compliance_report_csv_service_spec.rb
+++ b/ee/spec/services/groups/compliance_report_csv_service_spec.rb
@@ -2,10 +2,11 @@
 
 require 'spec_helper'
 
-RSpec.describe Groups::ComplianceReportCsvService, feature_category: :compliance_management do
+RSpec.describe Groups::ComplianceReportCsvService, :freeze_time, feature_category: :compliance_management do
   subject(:service) { described_class.new(user, group, filters) }
 
-  let(:filters) { { from: 10.years.ago, to: Time.current } }
+  let(:from) { 10.years.ago }
+  let(:filters) { { from: from, to: Time.current } }
 
   let_it_be(:user) { create(:user, name: 'John Cena') }
 
@@ -107,9 +108,9 @@
 
     context 'when verifying the csv data' do
       let(:all_commits) do
-        commits = project1.repository.commits(nil, limit: 100).map(&:sha) +
-          project2.repository.commits(nil, limit: 100).map(&:sha) +
-          sub_group_project.repository.commits(nil, limit: 100).map(&:sha)
+        commits = project1.repository.commits(nil, limit: 100, after: from).map(&:sha) +
+          project2.repository.commits(nil, limit: 100, after: from).map(&:sha) +
+          sub_group_project.repository.commits(nil, limit: 100, after: from).map(&:sha)
         commits.sort.uniq
       end
 
diff --git a/ee/spec/services/security/scan_result_policies/update_approvals_service_spec.rb b/ee/spec/services/security/scan_result_policies/update_approvals_service_spec.rb
index 80ca76f176967589fa38fa1d9e80a0675a10e13d..c132eaed8f61a7cde95d1a583b73ade9c1047b51 100644
--- a/ee/spec/services/security/scan_result_policies/update_approvals_service_spec.rb
+++ b/ee/spec/services/security/scan_result_policies/update_approvals_service_spec.rb
@@ -217,7 +217,7 @@
               message: 'Evaluating MR approval rules from scan result policies',
               pipeline_ids: [pipeline.id],
               target_pipeline_ids: [target_pipeline.id],
-              project_path: 'namespace1/project-1'
+              project_path: project.full_path
             ).and_call_original
 
           expect(::Gitlab::AppJsonLogger)
@@ -230,7 +230,7 @@
               merge_request_id: merge_request.id,
               merge_request_iid: merge_request.iid,
               reason: 'scan_finding rule violated',
-              project_path: 'namespace1/project-1'
+              project_path: project.full_path
             ).and_call_original
 
           execute
diff --git a/lib/gitlab/sidekiq_config.rb b/lib/gitlab/sidekiq_config.rb
index 33a15d95d225d7dcebe25e53784222bdbd8a65c6..b2ff80b235715142874be53ee00bf961250d4af7 100644
--- a/lib/gitlab/sidekiq_config.rb
+++ b/lib/gitlab/sidekiq_config.rb
@@ -40,6 +40,7 @@ module SidekiqConfig
 
     class << self
       include Gitlab::SidekiqConfig::CliMethods
+      include Gitlab::Utils::StrongMemoize
 
       def redis_queues
         # Not memoized, because this can change during the life of the application
@@ -54,28 +55,27 @@ def config_queues
       end
 
       def cron_jobs
-        @cron_jobs ||= begin
-          Gitlab.config.load_dynamic_cron_schedules!
-
-          jobs = Gitlab.config.cron_jobs.to_hash
-
-          jobs.delete('poll_interval') # Would be interpreted as a job otherwise
-
-          # Settingslogic (former gem used for yaml configuration) didn't allow 'class' key
-          # Therefore, we configure cron jobs with `job_class` as a workaround.
-          required_keys = %w[job_class cron]
-          jobs.each do |k, v|
-            if jobs[k] && required_keys.all? { |s| jobs[k].key?(s) }
-              jobs[k]['class'] = jobs[k].delete('job_class')
-            else
-              jobs.delete(k)
-              Gitlab::AppLogger.error("Invalid cron_jobs config key: '#{k}'. Check your gitlab config file.")
-            end
-          end
+        Gitlab.config.load_dynamic_cron_schedules!
+
+        jobs = Gitlab.config.cron_jobs.to_hash
+
+        jobs.delete('poll_interval') # Would be interpreted as a job otherwise
 
-          jobs
+        # Settingslogic (former gem used for yaml configuration) didn't allow 'class' key
+        # Therefore, we configure cron jobs with `job_class` as a workaround.
+        required_keys = %w[job_class cron]
+        jobs.each do |k, v|
+          if jobs[k] && required_keys.all? { |s| jobs[k].key?(s) }
+            jobs[k]['class'] = jobs[k].delete('job_class')
+          else
+            jobs.delete(k)
+            Gitlab::AppLogger.error("Invalid cron_jobs config key: '#{k}'. Check your gitlab config file.")
+          end
         end
+
+        jobs
       end
+      strong_memoize_attr :cron_jobs
 
       def cron_workers
         @cron_workers ||= cron_jobs.map { |job_name, options| options['class'].constantize }
diff --git a/qa/gdk/Dockerfile.gdk b/qa/gdk/Dockerfile.gdk
index 18902b57068a0f62ecb49820b8b4decae0766df1..adf1b330cb7de41b51329039ae4d72a21a932fbf 100644
--- a/qa/gdk/Dockerfile.gdk
+++ b/qa/gdk/Dockerfile.gdk
@@ -1,4 +1,8 @@
-FROM registry.gitlab.com/gitlab-org/gitlab-development-kit/asdf-bootstrapped-verify:main@sha256:fb833ce8f838e38104ee3b499a99f86e7a5844a4d4d8fdeeababd2d717504f3e as base
+ARG GDK_SHA=e8a2f3c30d3031c5574957efbaa917bacdeb3f1a
+# Use tag prefix when running on 'stable' branch to make sure 'protected' image is used which is not deleted by registry cleanup
+ARG GDK_BASE_TAG_PREFIX
+
+FROM registry.gitlab.com/gitlab-org/gitlab-development-kit/asdf-bootstrapped-verify/main:${GDK_BASE_TAG_PREFIX}${GDK_SHA} as base
 
 ENV GITLAB_LICENSE_MODE=test \
     GDK_KILL_CONFIRM=true
@@ -53,7 +57,7 @@ RUN set -eux; \
 #
 FROM base as gitlab-gems
 
-COPY --chown=gdk:gdk Gemfile Gemfile.lock ./gitlab/
+COPY --chown=gdk:gdk Gemfile Gemfile.lock .tool-versions ./gitlab/
 COPY --chown=gdk:gdk vendor/gems/ ./gitlab/vendor/gems/
 COPY --chown=gdk:gdk gems/ ./gitlab/gems/
 RUN make .gitlab-bundle \
diff --git a/scripts/build_gdk_image b/scripts/build_gdk_image
index cb1dbd03adb4125b2187748583cd65afd4f0cc91..894f888becef18afca61b8290574fd0fe243bfb9 100755
--- a/scripts/build_gdk_image
+++ b/scripts/build_gdk_image
@@ -7,6 +7,7 @@ source "$(dirname "$0")/utils.sh"
 REGISTRY="${CI_REGISTRY}/${CI_PROJECT_PATH}"
 SHA_TAG="${CI_COMMIT_SHA}"
 BRANCH_TAG="${CI_COMMIT_REF_SLUG}"
+STABLE_BRANCH_PATTERN="^[\d-]+-stable-ee$"
 
 IMAGE="${REGISTRY}/gitlab-qa-gdk"
 
@@ -16,6 +17,10 @@ else
   OUTPUT_OPTION="--load"
 fi
 
+if [[ "$BRANCH_TAG" =~ $STABLE_BRANCH_PATTERN || "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" =~ $STABLE_BRANCH_PATTERN ]]; then
+  GDK_BASE_TAG_PREFIX="stable-"
+fi
+
 echoinfo "Building GDK image" "yes"
 
 docker buildx build \
@@ -24,6 +29,7 @@ docker buildx build \
   --cache-from="${IMAGE}/cache:master" \
   --file="qa/gdk/Dockerfile.gdk" \
   --platform=${ARCH:-amd64} \
+  --build-arg "GDK_BASE_TAG_PREFIX=${GDK_BASE_TAG_PREFIX}" \
   --tag="${IMAGE}:${SHA_TAG}" \
   --tag="${IMAGE}:${BRANCH_TAG}" \
   ${OUTPUT_OPTION} \
diff --git a/spec/initializers/sidekiq_spec.rb b/spec/initializers/sidekiq_spec.rb
index a034e628d25972a5530760434bbdbc711f5d9bb4..fb1377244d207bef2327578032d89ed09050e4ee 100644
--- a/spec/initializers/sidekiq_spec.rb
+++ b/spec/initializers/sidekiq_spec.rb
@@ -78,11 +78,13 @@
     end
 
     around do |example|
+      Gitlab::SidekiqConfig.clear_memoization(:cron_jobs)
       original_settings = Gitlab.config['cron_jobs']
       Gitlab.config['cron_jobs'] = cron_jobs_settings
 
       example.run
 
+      Gitlab::SidekiqConfig.clear_memoization(:cron_jobs)
       Gitlab.config['cron_jobs'] = original_settings
     end
 
diff --git a/spec/lib/gitlab/sidekiq_config_spec.rb b/spec/lib/gitlab/sidekiq_config_spec.rb
index 00b1666106f5c8cb73324e4ef962254556d67e6e..5885151ecb58c2bbe72bdb3ab87fc7bc3dcbc4d3 100644
--- a/spec/lib/gitlab/sidekiq_config_spec.rb
+++ b/spec/lib/gitlab/sidekiq_config_spec.rb
@@ -18,6 +18,14 @@
   end
 
   describe '.cron_jobs' do
+    around do |example|
+      described_class.clear_memoization(:cron_jobs)
+
+      example.run
+
+      described_class.clear_memoization(:cron_jobs)
+    end
+
     it 'renames job_class to class and removes incomplete jobs' do
       expect(Gitlab)
         .to receive(:config)