diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh
index c233c50dcc78d47cb4caf05dec235133e83afe19..da27d63c84ebbf34ee6d7be56b0c1ca4b0c0645a 100755
--- a/Build/Scripts/runTests.sh
+++ b/Build/Scripts/runTests.sh
@@ -314,9 +314,8 @@ Options:
         Activate dry-run in CGL check that does not actively change files and only prints broken ones.
 
     -u
-        Update existing typo3/core-testing-*:latest container images and remove dangling local volumes.
-        New images are published once in a while and only the latest ones are supported by core testing.
-        Use this if weird test errors occur. Also removes obsolete image versions of typo3/core-testing-*.
+        Update existing typo3/core-testing-* container images and remove obsolete dangling image versions.
+        Use this if weird test errors occur.
 
     -h
         Show this help.
@@ -394,6 +393,13 @@ SUFFIX=$(echo $RANDOM)
 NETWORK="typo3-core-${SUFFIX}"
 CI_PARAMS=""
 CONTAINER_HOST="host.docker.internal"
+declare -A PHP_IMAGE_VERSIONS=(
+    ["7.4"]="2.4"
+    ["8.0"]="2.4"
+    ["8.1"]="2.7"
+    ["8.2"]="1.6"
+    ["8.3"]="1.7"
+)
 
 # Option parsing updates above default vars
 # Reset in case getopts has been used previously in the shell
@@ -432,7 +438,7 @@ while getopts ":a:b:s:c:d:i:p:e:xy:o:nhug" OPT; do
             ;;
         p)
             PHP_VERSION=${OPTARG}
-            if ! [[ ${PHP_VERSION} =~ ^(7.4|8.0|8.1|8.2|8.3)$ ]]; then
+            if [[ -z "${PHP_IMAGE_VERSIONS[${PHP_VERSION}]:-}" ]]; then
                 INVALID_OPTIONS+=("${OPTARG}")
             fi
             ;;
@@ -502,9 +508,9 @@ if ! type ${CONTAINER_BIN} >/dev/null 2>&1; then
     exit 1
 fi
 
-IMAGE_PHP="ghcr.io/typo3/core-testing-$(echo "php${PHP_VERSION}" | sed -e 's/\.//'):latest"
-IMAGE_NODEJS="ghcr.io/typo3/core-testing-js:latest"
-IMAGE_NODEJS_CHROME="ghcr.io/typo3/core-testing-js-chrome:latest"
+IMAGE_PHP="ghcr.io/typo3/core-testing-$(echo "php${PHP_VERSION}" | sed -e 's/\.//'):${PHP_IMAGE_VERSIONS[$PHP_VERSION]}"
+IMAGE_NODEJS="ghcr.io/typo3/core-testing-js:1.3"
+IMAGE_NODEJS_CHROME="ghcr.io/typo3/core-testing-js-chrome:1.3"
 IMAGE_ALPINE="docker.io/alpine:3.8"
 IMAGE_SELENIUM="docker.io/selenium/standalone-chrome:4.0.0-20211102"
 IMAGE_REDIS="docker.io/redis:4-alpine"
@@ -955,13 +961,13 @@ case ${TEST_SUITE} in
         SUITE_EXIT_CODE=$?
         ;;
     update)
-        # pull typo3/core-testing-*:latest versions of those ones that exist locally
-        echo "> pull ghcr.io/typo3/core-testing-*:latest versions of those ones that exist locally"
-        ${CONTAINER_BIN} images ghcr.io/typo3/core-testing-*:latest --format "{{.Repository}}:latest" | xargs -I {} ${CONTAINER_BIN} pull {}
+        # pull typo3/core-testing-* versions of those ones that exist locally
+        echo "> pull ghcr.io/typo3/core-testing-* versions of those ones that exist locally"
+        ${CONTAINER_BIN} images "ghcr.io/typo3/core-testing-*" --format "{{.Repository}}:{{.Tag}}" | xargs -I {} ${CONTAINER_BIN} pull {}
         echo ""
         # remove "dangling" typo3/core-testing-* images (those tagged as <none>)
         echo "> remove \"dangling\" ghcr.io/typo3/core-testing-* images (those tagged as <none>)"
-        ${CONTAINER_BIN} images ghcr.io/typo3/core-testing-* --filter "dangling=true" --format "{{.ID}}" | xargs -I {} ${CONTAINER_BIN} rmi -f {}
+        ${CONTAINER_BIN} images "ghcr.io/typo3/core-testing-*" --filter "dangling=true" --format "{{.ID}}" | xargs -I {} ${CONTAINER_BIN} rmi -f {}
         echo ""
         ;;
     *)