diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index bad5c1e429fb06c5cae82807e65c0442785c901b..6129f82b6fa65e4944e8deab844efaa9c39dc2e0 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -34,6 +34,7 @@ stages:
   - build
   - test
   - deploy
+  - cleanup
 
 workflow:
   rules:
@@ -361,15 +362,6 @@ deploy_knightly:
         openstack server add floating ip $NEW_INSTANCE_ID $CAMPUS_IP
         openstack server add floating ip $NEW_INSTANCE_ID $CHEAHA_IP
       fi
-    - |
-      SERVER_TO_BE_DELETE=($(openstack server list --name $OOD_INSTANCE_NAME --sort-column Image --sort-descending -f value -c ID | sed -n $(($NUM_SERVER_TO_KEEP+1))',$p'))
-      IMAGE_TO_BE_DELETE=($(openstack image list --sort-column Name --sort-descending -f value -c Name -c ID | grep -P ' ood-\d{8}$' | sed -n $(($NUM_IMAGE_TO_KEEP+1))',$p' | awk '{print $1}'))
-      for svr in ${SERVER_TO_BE_DELETE[@]}; do
-        openstack server delete ${svr}
-      done
-      for img in ${IMAGE_TO_BE_DELETE[@]}; do
-        openstack image delete ${img}
-      done
   only:
     - schedules
 
@@ -384,3 +376,22 @@ deploy_cheaha:
   when: manual
   only:
     - main
+
+cleanup:
+  stage: cleanup
+  environment:
+    name: knightly
+  tags:
+    - build
+  script:
+    - |
+      SERVER_TO_BE_DELETE=($(openstack server list --name $OOD_INSTANCE_NAME --sort-column Image --sort-descending -f value -c ID | sed -n $(($NUM_SERVER_TO_KEEP+1))',$p'))
+      IMAGE_TO_BE_DELETE=($(openstack image list --sort-column Name --sort-descending -f value -c Name -c ID | grep -P ' ood-\d{8}$' | sed -n $(($NUM_IMAGE_TO_KEEP+1))',$p' | awk '{print $1}'))
+      for svr in ${SERVER_TO_BE_DELETE[@]}; do
+        openstack server delete ${svr}
+      done
+      for img in ${IMAGE_TO_BE_DELETE[@]}; do
+        openstack image delete ${img}
+      done
+  allow_failure: true
+  when: always