diff --git a/renovate.json b/renovate.json
index 93bf4840930b378d28046f65e005e5eff8abd1ce..6e45fbadc6189a64345f16198b9a63e349b0cfd4 100644
--- a/renovate.json
+++ b/renovate.json
@@ -39,6 +39,18 @@
       "matchDepNames": ["ghcr.io/renovatebot/base-image"],
       "matchUpdateTypes": ["major", "minor"],
       "semanticCommitType": "feat"
+    },
+    {
+      "description": "set build scope for node updates",
+      "matchPackageNames": ["node"],
+      "matchFileNames": [".nvmrc", "tools/docker/Dockerfile"],
+      "semanticCommitType": "build"
+    },
+    {
+      "description": "set branch prefix for node major updates to split from minor updates",
+      "matchPackageNames": ["node"],
+      "matchUpdateTypes": ["major"],
+      "additionalBranchPrefix": "major"
     }
   ],
   "customManagers": [
diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile
index 64879bd7b668ebdb514924ab19efc822d7419c98..5c1c8beb058f0262b0250b85157bfbebb0295628 100644
--- a/tools/docker/Dockerfile
+++ b/tools/docker/Dockerfile
@@ -15,6 +15,12 @@ FROM ghcr.io/renovatebot/base-image:2.28.0-full@sha256:0e3a859452ece1442faa693c7
 # --------------------------------------
 FROM --platform=$BUILDPLATFORM ghcr.io/renovatebot/base-image:2.28.0@sha256:0abd37cc5818199aad69b226537fade5047bae1ca1a987f855453d9eed19a795 AS build
 
+# We want a specific node version here
+# renovate: datasource=node-version
+RUN install-tool node 20.15.1
+
+WORKDIR /usr/local/renovate
+
 ARG TARGETPLATFORM
 ARG BUILDPLATFORM
 
@@ -23,11 +29,21 @@ RUN set -ex; \
   uname -a; \
   true
 
-WORKDIR /usr/local/renovate
+# fetch static node binary
+# trim `linux/` from TARGETPLATFORM
+# replace `amd64` with `x64` for `node`
+RUN set -ex; \
+  ver=$(node --version); \
+  arch=${TARGETPLATFORM:6}; \
+  temp_dir="$(mktemp -d)"; \
+  curl -fsSL https://nodejs.org/dist/${ver}/node-${ver}-linux-${arch/amd64/x64}.tar.xz -o ${temp_dir}/node.tar.xz; \
+  bsdtar --strip 1 -C ${temp_dir} -xf ${temp_dir}/node.tar.xz; \
+  cp ${temp_dir}/bin/node ./node; \
+  true
 
+# fetch npm packages
 ENV CI=1 npm_config_modules_cache_max_age=0 \
-  npm_config_loglevel=info \
-  ARCH=arm64
+  npm_config_loglevel=info
 
 COPY pnpm-lock.yaml ./
 
@@ -44,20 +60,11 @@ RUN set -ex; \
 
 COPY . ./
 
-# install
+# install npm packages
 RUN set -ex; \
   corepack pnpm install --prod --offline --ignore-scripts; \
   true
 
-# test
-COPY tools/docker/bin/ /usr/local/bin/
-# RE2 doesn't work on cross compile
-ENV RENOVATE_X_IGNORE_RE2=true
-RUN set -ex; \
-  renovate --version; \
-  renovate-config-validator; \
-  true
-
 # --------------------------------------
 # final image
 # --------------------------------------
@@ -87,7 +94,7 @@ RUN set -ex; \
 
 RUN set -ex; \
   renovate --version; \
-  node -e "new require('re2')('.*').exec('test');new require('better-sqlite3')(':memory:')"; \
+  /usr/local/renovate/node -e "new require('re2')('.*').exec('test');new require('better-sqlite3')(':memory:')"; \
   true
 
 LABEL \
diff --git a/tools/docker/bin/renovate b/tools/docker/bin/renovate
index 1b62a5a3b2ed3c7b4d00a7e1e8478560cbd0b1be..2b6c69c867ad3d86d43782399c168e001f54efc5 100755
--- a/tools/docker/bin/renovate
+++ b/tools/docker/bin/renovate
@@ -5,4 +5,4 @@ if [[ -f "/usr/local/etc/env" && -z "${CONTAINERBASE_ENV+x}" ]]; then
   . /usr/local/etc/env
 fi
 
-node "${RENOVATE_NODE_ARGS[@]}" /usr/local/renovate/dist/renovate.js "$@"
+/usr/local/renovate/node "${RENOVATE_NODE_ARGS[@]}" /usr/local/renovate/dist/renovate.js "$@"
diff --git a/tools/docker/bin/renovate-config-validator b/tools/docker/bin/renovate-config-validator
index ba165e6d2d9713f0b8ad67181b12a938c135c9f4..0ca2f9db9fbbe5e6c2ea6c0d538ae9b9fb14096f 100755
--- a/tools/docker/bin/renovate-config-validator
+++ b/tools/docker/bin/renovate-config-validator
@@ -5,4 +5,4 @@ if [[ -f "/usr/local/etc/env" && -z "${CONTAINERBASE_ENV+x}" ]]; then
   . /usr/local/etc/env
 fi
 
-node /usr/local/renovate/dist/config-validator.js "$@"
+/usr/local/renovate/node /usr/local/renovate/dist/config-validator.js "$@"