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 56396e82f9214a6b7abc3c734a165234bd758fb1..7cecb51b9a50ad4fbf7d0a5efc13af61f7c0f138 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -925,6 +925,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/qa/gdk/Dockerfile.gdk b/qa/gdk/Dockerfile.gdk
index 6cc2c6a3ed4defc6ba946a05febd5f4f438b92c2..0177b649e7ea4a0e4556efac68bfb44a332e68b9 100644
--- a/qa/gdk/Dockerfile.gdk
+++ b/qa/gdk/Dockerfile.gdk
@@ -1,6 +1,8 @@
 ARG GDK_SHA=fb96a94cf54d4b0c5db2a3bdf905874dbbc1cf67
+# 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_SHA} as base
+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
diff --git a/scripts/build_gdk_image b/scripts/build_gdk_image
index 3401c8df86cfe7b007d788675d64effffbac92da..58b1ef6fd05c07f60e7d69f4316c6bdf1efc5ef9 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}" \
   --provenance=false \