diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index eba9ea0588c8f6f0d8c914308a634181329d9aee..e9cf324654137fc2aa97d132bff39a3117b6424a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -5,6 +5,8 @@ stages:
   - test
   - cleanup
 
+image: "${CI_REGISTRY_IMAGE}/bootstrap-ci/${CI_COMMIT_REF_NAME}"
+
 ci_test_image:
   stage: build
   variables:
@@ -16,8 +18,8 @@ ci_test_image:
     - docker info
   script:
     - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
-    - docker build -t ${CI_REGISTRY_IMAGE}/bootstrap-ci test/
-    - docker push ${CI_REGISTRY_IMAGE}/bootstrap-ci
+    - docker build -t ${CI_REGISTRY_IMAGE}/bootstrap-ci/${CI_COMMIT_REF_NAME} test/
+    - docker push ${CI_REGISTRY_IMAGE}/bootstrap-ci/${CI_COMMIT_REF_NAME}
   only:
     changes:
       - test/Dockerfile
@@ -25,7 +27,6 @@ ci_test_image:
 
 bootstrap:
   stage: setup-cluster
-  image: "${CI_REGISTRY_IMAGE}/bootstrap-ci"
   script:
     # Ensure test/ is not world-writable otherwise ansible-playbook refuses to run, see
     # https://docs.ansible.com/ansible/devel/reference_appendices/config.html#cfg-in-world-writable-dir
@@ -44,7 +45,6 @@ bootstrap:
 
 install:
   stage: install-apps
-  image: "${CI_REGISTRY_IMAGE}/bootstrap-ci"
   script:
     - cd test/
     - eval $(ssh-agent -s)
@@ -57,7 +57,6 @@ install:
 
 testinfra:
   stage: test
-  image: "${CI_REGISTRY_IMAGE}/bootstrap-ci"
   script:
     - eval $(ssh-agent -s)
     - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
@@ -66,7 +65,6 @@ testinfra:
 
 terminate:
   stage: cleanup
-  image: "${CI_REGISTRY_IMAGE}/bootstrap-ci"
   script:
     # Remove droplet after successful tests
     - cd test/