From e021d1b2cdf479933a1e8067b8df7266b44427a9 Mon Sep 17 00:00:00 2001 From: Vlad Frangu Date: Sun, 14 Jun 2026 23:35:46 +0300 Subject: [PATCH 1/3] feat(node): centralize package-manager caches and add corepack Route npm, yarn, and pnpm caches to a single world-writable /pkg-cache/ tree (out of $HOME) so they can be wiped or tmpfs-mounted to reclaim space, install the latest Corepack globally so Actors can use yarn or pnpm, and make pnpm use the hoisted (npm-style) node_modules layout instead of symlinks. Applies to all built Node images: node, node-playwright(-chrome/-firefox/ -webkit/-camoufox), and node-puppeteer-chrome. --- node-playwright-camoufox/Dockerfile | 23 +++++++++++++++++++++++ node-playwright-chrome/Dockerfile | 23 +++++++++++++++++++++++ node-playwright-firefox/Dockerfile | 23 +++++++++++++++++++++++ node-playwright-webkit/Dockerfile | 23 +++++++++++++++++++++++ node-playwright/Dockerfile | 23 +++++++++++++++++++++++ node-puppeteer-chrome/Dockerfile | 23 +++++++++++++++++++++++ node/Dockerfile | 25 ++++++++++++++++++++++++- 7 files changed, 162 insertions(+), 1 deletion(-) diff --git a/node-playwright-camoufox/Dockerfile b/node-playwright-camoufox/Dockerfile index eaaeab77..fbcef67c 100644 --- a/node-playwright-camoufox/Dockerfile +++ b/node-playwright-camoufox/Dockerfile @@ -66,6 +66,15 @@ RUN apt update \ # Globally disable the update-notifier. && npm config --global set update-notifier false \ \ + # Install the latest Corepack so Actors can opt into yarn or pnpm; `enable` drops the shims into the global bin. + && npm install -g corepack@latest \ + && corepack enable \ + \ + # Pre-create the shared package-manager cache dirs (see the *_CACHE_*/*_STORE_DIR env vars), world-writable so any + # user can populate them and `rm -rf` them at the end to reclaim space, regardless of which user the Actor runs as. + && mkdir -p /pkg-cache/npm /pkg-cache/yarn /pkg-cache/pnpm/store /pkg-cache/pnpm/cache \ + && chmod -R 777 /pkg-cache \ + \ # Cleanup time && rm -rf /var/lib/apt/lists/* \ && rm -rf /src/*.deb \ @@ -90,6 +99,20 @@ ENV NODE_ENV=production # so we put back the old limit of 80kb, which seems to work just fine. ENV NODE_OPTIONS="--max_old_space_size=30000 --max-http-header-size=80000" +# Send every package-manager cache to a single, dedicated location instead of $HOME. +# npm, yarn, and pnpm have no real "disable the cache" switch (unlike pip), so isolating +# their caches under /pkg-cache is the practical equivalent: the tree holds only throwaway +# cache data and can be wiped or mounted as tmpfs without touching installed dependencies. +ENV NPM_CONFIG_CACHE=/pkg-cache/npm \ + YARN_CACHE_FOLDER=/pkg-cache/yarn \ + YARN_GLOBAL_FOLDER=/pkg-cache/yarn \ + PNPM_CONFIG_STORE_DIR=/pkg-cache/pnpm/store \ + PNPM_CONFIG_CACHE_DIR=/pkg-cache/pnpm/cache + +# Make pnpm use a flat, npm-style node_modules (the `hoisted` linker) instead of its +# default symlinked layout, so installed dependencies are plain copies with no symlinks. +ENV PNPM_CONFIG_NODE_LINKER=hoisted + # Install default dependencies, print versions of everything RUN npm --quiet set progress=false \ \ diff --git a/node-playwright-chrome/Dockerfile b/node-playwright-chrome/Dockerfile index 5c073a7a..04a55073 100644 --- a/node-playwright-chrome/Dockerfile +++ b/node-playwright-chrome/Dockerfile @@ -81,6 +81,15 @@ RUN apt update \ # Globally disable the update-notifier. && npm config --global set update-notifier false \ \ + # Install the latest Corepack so Actors can opt into yarn or pnpm; `enable` drops the shims into the global bin. + && npm install -g corepack@latest \ + && corepack enable \ + \ + # Pre-create the shared package-manager cache dirs (see the *_CACHE_*/*_STORE_DIR env vars), world-writable so any + # user can populate them and `rm -rf` them at the end to reclaim space, regardless of which user the Actor runs as. + && mkdir -p /pkg-cache/npm /pkg-cache/yarn /pkg-cache/pnpm/store /pkg-cache/pnpm/cache \ + && chmod -R 777 /pkg-cache \ + \ # Cleanup time && rm -rf /var/lib/apt/lists/* \ && rm -rf /src/*.deb \ @@ -105,6 +114,20 @@ ENV NODE_ENV=production # so we put back the old limit of 80kb, which seems to work just fine. ENV NODE_OPTIONS="--max_old_space_size=30000 --max-http-header-size=80000" +# Send every package-manager cache to a single, dedicated location instead of $HOME. +# npm, yarn, and pnpm have no real "disable the cache" switch (unlike pip), so isolating +# their caches under /pkg-cache is the practical equivalent: the tree holds only throwaway +# cache data and can be wiped or mounted as tmpfs without touching installed dependencies. +ENV NPM_CONFIG_CACHE=/pkg-cache/npm \ + YARN_CACHE_FOLDER=/pkg-cache/yarn \ + YARN_GLOBAL_FOLDER=/pkg-cache/yarn \ + PNPM_CONFIG_STORE_DIR=/pkg-cache/pnpm/store \ + PNPM_CONFIG_CACHE_DIR=/pkg-cache/pnpm/cache + +# Make pnpm use a flat, npm-style node_modules (the `hoisted` linker) instead of its +# default symlinked layout, so installed dependencies are plain copies with no symlinks. +ENV PNPM_CONFIG_NODE_LINKER=hoisted + # Install default dependencies, print versions of everything RUN npm --quiet set progress=false \ && npm install --omit=dev --omit=optional --no-package-lock --prefer-online \ diff --git a/node-playwright-firefox/Dockerfile b/node-playwright-firefox/Dockerfile index 594c52ed..e3e3d96f 100644 --- a/node-playwright-firefox/Dockerfile +++ b/node-playwright-firefox/Dockerfile @@ -71,6 +71,15 @@ RUN apt update \ # Globally disable the update-notifier. && npm config --global set update-notifier false \ \ + # Install the latest Corepack so Actors can opt into yarn or pnpm; `enable` drops the shims into the global bin. + && npm install -g corepack@latest \ + && corepack enable \ + \ + # Pre-create the shared package-manager cache dirs (see the *_CACHE_*/*_STORE_DIR env vars), world-writable so any + # user can populate them and `rm -rf` them at the end to reclaim space, regardless of which user the Actor runs as. + && mkdir -p /pkg-cache/npm /pkg-cache/yarn /pkg-cache/pnpm/store /pkg-cache/pnpm/cache \ + && chmod -R 777 /pkg-cache \ + \ # Cleanup time && rm -rf /var/lib/apt/lists/* \ && rm -rf /src/*.deb \ @@ -95,6 +104,20 @@ ENV NODE_ENV=production # so we put back the old limit of 80kb, which seems to work just fine. ENV NODE_OPTIONS="--max_old_space_size=30000 --max-http-header-size=80000" +# Send every package-manager cache to a single, dedicated location instead of $HOME. +# npm, yarn, and pnpm have no real "disable the cache" switch (unlike pip), so isolating +# their caches under /pkg-cache is the practical equivalent: the tree holds only throwaway +# cache data and can be wiped or mounted as tmpfs without touching installed dependencies. +ENV NPM_CONFIG_CACHE=/pkg-cache/npm \ + YARN_CACHE_FOLDER=/pkg-cache/yarn \ + YARN_GLOBAL_FOLDER=/pkg-cache/yarn \ + PNPM_CONFIG_STORE_DIR=/pkg-cache/pnpm/store \ + PNPM_CONFIG_CACHE_DIR=/pkg-cache/pnpm/cache + +# Make pnpm use a flat, npm-style node_modules (the `hoisted` linker) instead of its +# default symlinked layout, so installed dependencies are plain copies with no symlinks. +ENV PNPM_CONFIG_NODE_LINKER=hoisted + # Install default dependencies, print versions of everything RUN npm --quiet set progress=false \ && npm install --omit=dev --omit=optional --no-package-lock --prefer-online \ diff --git a/node-playwright-webkit/Dockerfile b/node-playwright-webkit/Dockerfile index 62bf8e71..3786810a 100644 --- a/node-playwright-webkit/Dockerfile +++ b/node-playwright-webkit/Dockerfile @@ -60,6 +60,15 @@ RUN apt update \ # Globally disable the update-notifier. && npm config --global set update-notifier false \ \ + # Install the latest Corepack so Actors can opt into yarn or pnpm; `enable` drops the shims into the global bin. + && npm install -g corepack@latest \ + && corepack enable \ + \ + # Pre-create the shared package-manager cache dirs (see the *_CACHE_*/*_STORE_DIR env vars), world-writable so any + # user can populate them and `rm -rf` them at the end to reclaim space, regardless of which user the Actor runs as. + && mkdir -p /pkg-cache/npm /pkg-cache/yarn /pkg-cache/pnpm/store /pkg-cache/pnpm/cache \ + && chmod -R 777 /pkg-cache \ + \ # Cleanup time && rm -rf /var/lib/apt/lists/* \ && rm -rf /src/*.deb \ @@ -84,6 +93,20 @@ ENV NODE_ENV=production # so we put back the old limit of 80kb, which seems to work just fine. ENV NODE_OPTIONS="--max_old_space_size=30000 --max-http-header-size=80000" +# Send every package-manager cache to a single, dedicated location instead of $HOME. +# npm, yarn, and pnpm have no real "disable the cache" switch (unlike pip), so isolating +# their caches under /pkg-cache is the practical equivalent: the tree holds only throwaway +# cache data and can be wiped or mounted as tmpfs without touching installed dependencies. +ENV NPM_CONFIG_CACHE=/pkg-cache/npm \ + YARN_CACHE_FOLDER=/pkg-cache/yarn \ + YARN_GLOBAL_FOLDER=/pkg-cache/yarn \ + PNPM_CONFIG_STORE_DIR=/pkg-cache/pnpm/store \ + PNPM_CONFIG_CACHE_DIR=/pkg-cache/pnpm/cache + +# Make pnpm use a flat, npm-style node_modules (the `hoisted` linker) instead of its +# default symlinked layout, so installed dependencies are plain copies with no symlinks. +ENV PNPM_CONFIG_NODE_LINKER=hoisted + # Install default dependencies, print versions of everything RUN npm --quiet set progress=false \ && npm install --omit=dev --omit=optional --no-package-lock --prefer-online \ diff --git a/node-playwright/Dockerfile b/node-playwright/Dockerfile index bc65c758..2df2e42c 100644 --- a/node-playwright/Dockerfile +++ b/node-playwright/Dockerfile @@ -83,6 +83,15 @@ RUN apt update \ # Globally disable the update-notifier. && npm config --global set update-notifier false \ \ + # Install the latest Corepack so Actors can opt into yarn or pnpm; `enable` drops the shims into the global bin. + && npm install -g corepack@latest \ + && corepack enable \ + \ + # Pre-create the shared package-manager cache dirs (see the *_CACHE_*/*_STORE_DIR env vars), world-writable so any + # user can populate them and `rm -rf` them at the end to reclaim space, regardless of which user the Actor runs as. + && mkdir -p /pkg-cache/npm /pkg-cache/yarn /pkg-cache/pnpm/store /pkg-cache/pnpm/cache \ + && chmod -R 777 /pkg-cache \ + \ # Cleanup time && rm -rf /var/lib/apt/lists/* \ && rm -rf /src/*.deb \ @@ -107,6 +116,20 @@ ENV NODE_ENV=production # so we put back the old limit of 80kb, which seems to work just fine. ENV NODE_OPTIONS="--max_old_space_size=30000 --max-http-header-size=80000" +# Send every package-manager cache to a single, dedicated location instead of $HOME. +# npm, yarn, and pnpm have no real "disable the cache" switch (unlike pip), so isolating +# their caches under /pkg-cache is the practical equivalent: the tree holds only throwaway +# cache data and can be wiped or mounted as tmpfs without touching installed dependencies. +ENV NPM_CONFIG_CACHE=/pkg-cache/npm \ + YARN_CACHE_FOLDER=/pkg-cache/yarn \ + YARN_GLOBAL_FOLDER=/pkg-cache/yarn \ + PNPM_CONFIG_STORE_DIR=/pkg-cache/pnpm/store \ + PNPM_CONFIG_CACHE_DIR=/pkg-cache/pnpm/cache + +# Make pnpm use a flat, npm-style node_modules (the `hoisted` linker) instead of its +# default symlinked layout, so installed dependencies are plain copies with no symlinks. +ENV PNPM_CONFIG_NODE_LINKER=hoisted + # Install default dependencies, print versions of everything RUN npm --quiet set progress=false \ && npm install --omit=dev --omit=optional --no-package-lock --prefer-online \ diff --git a/node-puppeteer-chrome/Dockerfile b/node-puppeteer-chrome/Dockerfile index 029d82c7..59f1ac72 100644 --- a/node-puppeteer-chrome/Dockerfile +++ b/node-puppeteer-chrome/Dockerfile @@ -90,6 +90,15 @@ RUN apt update \ \ # Globally disable the update-notifier. && npm config --global set update-notifier false \ + \ + # Install the latest Corepack so Actors can opt into yarn or pnpm; `enable` drops the shims into the global bin. + && npm install -g corepack@latest \ + && corepack enable \ + \ + # Pre-create the shared package-manager cache dirs (see the *_CACHE_*/*_STORE_DIR env vars), world-writable so any + # user can populate them and `rm -rf` them at the end to reclaim space, regardless of which user the Actor runs as. + && mkdir -p /pkg-cache/npm /pkg-cache/yarn /pkg-cache/pnpm/store /pkg-cache/pnpm/cache \ + && chmod -R 777 /pkg-cache \ # Cleanup && rm -rf /var/lib/apt/lists/* \ && rm -rf /src/*.deb \ @@ -114,6 +123,20 @@ ENV NODE_ENV=production # so we put back the old limit of 80kb, which seems to work just fine. ENV NODE_OPTIONS="--max_old_space_size=30000 --max-http-header-size=80000" +# Send every package-manager cache to a single, dedicated location instead of $HOME. +# npm, yarn, and pnpm have no real "disable the cache" switch (unlike pip), so isolating +# their caches under /pkg-cache is the practical equivalent: the tree holds only throwaway +# cache data and can be wiped or mounted as tmpfs without touching installed dependencies. +ENV NPM_CONFIG_CACHE=/pkg-cache/npm \ + YARN_CACHE_FOLDER=/pkg-cache/yarn \ + YARN_GLOBAL_FOLDER=/pkg-cache/yarn \ + PNPM_CONFIG_STORE_DIR=/pkg-cache/pnpm/store \ + PNPM_CONFIG_CACHE_DIR=/pkg-cache/pnpm/cache + +# Make pnpm use a flat, npm-style node_modules (the `hoisted` linker) instead of its +# default symlinked layout, so installed dependencies are plain copies with no symlinks. +ENV PNPM_CONFIG_NODE_LINKER=hoisted + # Install default dependencies, print versions of everything RUN npm --quiet set progress=false \ && npm install --omit=dev --omit=optional --no-package-lock --prefer-online \ diff --git a/node/Dockerfile b/node/Dockerfile index f1ec68be..c04dc3a4 100644 --- a/node/Dockerfile +++ b/node/Dockerfile @@ -14,13 +14,36 @@ RUN npm config --global set update-notifier false \ && chown -R myuser:myuser /home/myuser \ && mkdir -p /usr/src/app \ && chown -R myuser:myuser /usr/src/app \ - && ln -s /usr/src/app /home/myuser + && ln -s /usr/src/app /home/myuser \ + \ + # Install the latest Corepack so Actors can opt into yarn or pnpm; `enable` drops the shims into the global bin. + && npm install -g corepack@latest \ + && corepack enable \ + \ + # Pre-create the shared package-manager cache dirs (see the *_CACHE_*/*_STORE_DIR env vars), world-writable so any + # user can populate them and `rm -rf` them at the end to reclaim space, regardless of which user the Actor runs as. + && mkdir -p /pkg-cache/npm /pkg-cache/yarn /pkg-cache/pnpm/store /pkg-cache/pnpm/cache \ + && chmod -R 777 /pkg-cache WORKDIR /usr/src/app # Copy source code COPY --chown=myuser:myuser package.json main.js . +# Send every package-manager cache to a single, dedicated location instead of $HOME. +# npm, yarn, and pnpm have no real "disable the cache" switch (unlike pip), so isolating +# their caches under /pkg-cache is the practical equivalent: the tree holds only throwaway +# cache data and can be wiped or mounted as tmpfs without touching installed dependencies. +ENV NPM_CONFIG_CACHE=/pkg-cache/npm \ + YARN_CACHE_FOLDER=/pkg-cache/yarn \ + YARN_GLOBAL_FOLDER=/pkg-cache/yarn \ + PNPM_CONFIG_STORE_DIR=/pkg-cache/pnpm/store \ + PNPM_CONFIG_CACHE_DIR=/pkg-cache/pnpm/cache + +# Make pnpm use a flat, npm-style node_modules (the `hoisted` linker) instead of its +# default symlinked layout, so installed dependencies are plain copies with no symlinks. +ENV PNPM_CONFIG_NODE_LINKER=hoisted + # Install default dependencies, print versions of everything RUN npm --quiet set progress=false \ && npm install --omit=dev --omit=optional --no-package-lock --prefer-online \ From 64816b468f531f8cf75270d1a566d20cd3fae188 Mon Sep 17 00:00:00 2001 From: Vlad Frangu Date: Tue, 4 Aug 2026 15:42:59 +0300 Subject: [PATCH 2/3] feat(node): default yarn to the node-modules linker Yarn Berry (what Corepack installs) defaults to Plug'n'Play; set YARN_NODE_LINKER=node-modules so yarn installs produce a plain node_modules tree, matching npm and the hoisted pnpm linker. --- node-playwright-camoufox/Dockerfile | 8 +++++--- node-playwright-chrome/Dockerfile | 8 +++++--- node-playwright-firefox/Dockerfile | 8 +++++--- node-playwright-webkit/Dockerfile | 8 +++++--- node-playwright/Dockerfile | 8 +++++--- node-puppeteer-chrome/Dockerfile | 8 +++++--- node/Dockerfile | 8 +++++--- 7 files changed, 35 insertions(+), 21 deletions(-) diff --git a/node-playwright-camoufox/Dockerfile b/node-playwright-camoufox/Dockerfile index fbcef67c..5c1aff05 100644 --- a/node-playwright-camoufox/Dockerfile +++ b/node-playwright-camoufox/Dockerfile @@ -109,9 +109,11 @@ ENV NPM_CONFIG_CACHE=/pkg-cache/npm \ PNPM_CONFIG_STORE_DIR=/pkg-cache/pnpm/store \ PNPM_CONFIG_CACHE_DIR=/pkg-cache/pnpm/cache -# Make pnpm use a flat, npm-style node_modules (the `hoisted` linker) instead of its -# default symlinked layout, so installed dependencies are plain copies with no symlinks. -ENV PNPM_CONFIG_NODE_LINKER=hoisted +# Make pnpm and yarn use a flat, npm-style node_modules instead of their non-node_modules +# defaults (pnpm's symlinked store, yarn Berry's Plug'n'Play), so installed dependencies are +# plain copies that any tooling can resolve without symlinks or a PnP loader. +ENV PNPM_CONFIG_NODE_LINKER=hoisted \ + YARN_NODE_LINKER=node-modules # Install default dependencies, print versions of everything RUN npm --quiet set progress=false \ diff --git a/node-playwright-chrome/Dockerfile b/node-playwright-chrome/Dockerfile index 04a55073..3c03b0ae 100644 --- a/node-playwright-chrome/Dockerfile +++ b/node-playwright-chrome/Dockerfile @@ -124,9 +124,11 @@ ENV NPM_CONFIG_CACHE=/pkg-cache/npm \ PNPM_CONFIG_STORE_DIR=/pkg-cache/pnpm/store \ PNPM_CONFIG_CACHE_DIR=/pkg-cache/pnpm/cache -# Make pnpm use a flat, npm-style node_modules (the `hoisted` linker) instead of its -# default symlinked layout, so installed dependencies are plain copies with no symlinks. -ENV PNPM_CONFIG_NODE_LINKER=hoisted +# Make pnpm and yarn use a flat, npm-style node_modules instead of their non-node_modules +# defaults (pnpm's symlinked store, yarn Berry's Plug'n'Play), so installed dependencies are +# plain copies that any tooling can resolve without symlinks or a PnP loader. +ENV PNPM_CONFIG_NODE_LINKER=hoisted \ + YARN_NODE_LINKER=node-modules # Install default dependencies, print versions of everything RUN npm --quiet set progress=false \ diff --git a/node-playwright-firefox/Dockerfile b/node-playwright-firefox/Dockerfile index e3e3d96f..f2eacaf2 100644 --- a/node-playwright-firefox/Dockerfile +++ b/node-playwright-firefox/Dockerfile @@ -114,9 +114,11 @@ ENV NPM_CONFIG_CACHE=/pkg-cache/npm \ PNPM_CONFIG_STORE_DIR=/pkg-cache/pnpm/store \ PNPM_CONFIG_CACHE_DIR=/pkg-cache/pnpm/cache -# Make pnpm use a flat, npm-style node_modules (the `hoisted` linker) instead of its -# default symlinked layout, so installed dependencies are plain copies with no symlinks. -ENV PNPM_CONFIG_NODE_LINKER=hoisted +# Make pnpm and yarn use a flat, npm-style node_modules instead of their non-node_modules +# defaults (pnpm's symlinked store, yarn Berry's Plug'n'Play), so installed dependencies are +# plain copies that any tooling can resolve without symlinks or a PnP loader. +ENV PNPM_CONFIG_NODE_LINKER=hoisted \ + YARN_NODE_LINKER=node-modules # Install default dependencies, print versions of everything RUN npm --quiet set progress=false \ diff --git a/node-playwright-webkit/Dockerfile b/node-playwright-webkit/Dockerfile index 3786810a..2ea05569 100644 --- a/node-playwright-webkit/Dockerfile +++ b/node-playwright-webkit/Dockerfile @@ -103,9 +103,11 @@ ENV NPM_CONFIG_CACHE=/pkg-cache/npm \ PNPM_CONFIG_STORE_DIR=/pkg-cache/pnpm/store \ PNPM_CONFIG_CACHE_DIR=/pkg-cache/pnpm/cache -# Make pnpm use a flat, npm-style node_modules (the `hoisted` linker) instead of its -# default symlinked layout, so installed dependencies are plain copies with no symlinks. -ENV PNPM_CONFIG_NODE_LINKER=hoisted +# Make pnpm and yarn use a flat, npm-style node_modules instead of their non-node_modules +# defaults (pnpm's symlinked store, yarn Berry's Plug'n'Play), so installed dependencies are +# plain copies that any tooling can resolve without symlinks or a PnP loader. +ENV PNPM_CONFIG_NODE_LINKER=hoisted \ + YARN_NODE_LINKER=node-modules # Install default dependencies, print versions of everything RUN npm --quiet set progress=false \ diff --git a/node-playwright/Dockerfile b/node-playwright/Dockerfile index 2df2e42c..d80d2aff 100644 --- a/node-playwright/Dockerfile +++ b/node-playwright/Dockerfile @@ -126,9 +126,11 @@ ENV NPM_CONFIG_CACHE=/pkg-cache/npm \ PNPM_CONFIG_STORE_DIR=/pkg-cache/pnpm/store \ PNPM_CONFIG_CACHE_DIR=/pkg-cache/pnpm/cache -# Make pnpm use a flat, npm-style node_modules (the `hoisted` linker) instead of its -# default symlinked layout, so installed dependencies are plain copies with no symlinks. -ENV PNPM_CONFIG_NODE_LINKER=hoisted +# Make pnpm and yarn use a flat, npm-style node_modules instead of their non-node_modules +# defaults (pnpm's symlinked store, yarn Berry's Plug'n'Play), so installed dependencies are +# plain copies that any tooling can resolve without symlinks or a PnP loader. +ENV PNPM_CONFIG_NODE_LINKER=hoisted \ + YARN_NODE_LINKER=node-modules # Install default dependencies, print versions of everything RUN npm --quiet set progress=false \ diff --git a/node-puppeteer-chrome/Dockerfile b/node-puppeteer-chrome/Dockerfile index 59f1ac72..a291240a 100644 --- a/node-puppeteer-chrome/Dockerfile +++ b/node-puppeteer-chrome/Dockerfile @@ -133,9 +133,11 @@ ENV NPM_CONFIG_CACHE=/pkg-cache/npm \ PNPM_CONFIG_STORE_DIR=/pkg-cache/pnpm/store \ PNPM_CONFIG_CACHE_DIR=/pkg-cache/pnpm/cache -# Make pnpm use a flat, npm-style node_modules (the `hoisted` linker) instead of its -# default symlinked layout, so installed dependencies are plain copies with no symlinks. -ENV PNPM_CONFIG_NODE_LINKER=hoisted +# Make pnpm and yarn use a flat, npm-style node_modules instead of their non-node_modules +# defaults (pnpm's symlinked store, yarn Berry's Plug'n'Play), so installed dependencies are +# plain copies that any tooling can resolve without symlinks or a PnP loader. +ENV PNPM_CONFIG_NODE_LINKER=hoisted \ + YARN_NODE_LINKER=node-modules # Install default dependencies, print versions of everything RUN npm --quiet set progress=false \ diff --git a/node/Dockerfile b/node/Dockerfile index c04dc3a4..b1009c18 100644 --- a/node/Dockerfile +++ b/node/Dockerfile @@ -40,9 +40,11 @@ ENV NPM_CONFIG_CACHE=/pkg-cache/npm \ PNPM_CONFIG_STORE_DIR=/pkg-cache/pnpm/store \ PNPM_CONFIG_CACHE_DIR=/pkg-cache/pnpm/cache -# Make pnpm use a flat, npm-style node_modules (the `hoisted` linker) instead of its -# default symlinked layout, so installed dependencies are plain copies with no symlinks. -ENV PNPM_CONFIG_NODE_LINKER=hoisted +# Make pnpm and yarn use a flat, npm-style node_modules instead of their non-node_modules +# defaults (pnpm's symlinked store, yarn Berry's Plug'n'Play), so installed dependencies are +# plain copies that any tooling can resolve without symlinks or a PnP loader. +ENV PNPM_CONFIG_NODE_LINKER=hoisted \ + YARN_NODE_LINKER=node-modules # Install default dependencies, print versions of everything RUN npm --quiet set progress=false \ From 53b83d6aa63cd71d14f79c8a5b6bd649e3c8d679 Mon Sep 17 00:00:00 2001 From: Vlad Frangu Date: Tue, 4 Aug 2026 15:46:20 +0300 Subject: [PATCH 3/3] feat(node): symlink ~/.npm to the shared package cache npm's default per-user cache lives in ~/.npm. Point /root/.npm and /home/myuser/.npm at /pkg-cache/npm so tooling that bypasses NPM_CONFIG_CACHE still hits the shared cache, and existing templates that run `rm -r ~/.npm` after install keep working. --- node-playwright-camoufox/Dockerfile | 8 +++++++- node-playwright-chrome/Dockerfile | 8 +++++++- node-playwright-firefox/Dockerfile | 8 +++++++- node-playwright-webkit/Dockerfile | 8 +++++++- node-playwright/Dockerfile | 8 +++++++- node-puppeteer-chrome/Dockerfile | 8 +++++++- node/Dockerfile | 8 +++++++- 7 files changed, 49 insertions(+), 7 deletions(-) diff --git a/node-playwright-camoufox/Dockerfile b/node-playwright-camoufox/Dockerfile index 5c1aff05..48b60657 100644 --- a/node-playwright-camoufox/Dockerfile +++ b/node-playwright-camoufox/Dockerfile @@ -81,7 +81,13 @@ RUN apt update \ && apt clean -y && apt autoremove -y \ && rm -rf /root/.npm \ # This is needed to remove an annoying error message when running headful. - && mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix + && mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix \ + \ + # Point the default per-user npm cache dirs (~/.npm) at the shared cache, so tooling + # that bypasses NPM_CONFIG_CACHE - and templates that `rm -r ~/.npm` - keep working. + && rm -rf /root/.npm /home/myuser/.npm \ + && ln -s /pkg-cache/npm /root/.npm \ + && ln -s /pkg-cache/npm /home/myuser/.npm # Run everything after as non-privileged user. USER myuser diff --git a/node-playwright-chrome/Dockerfile b/node-playwright-chrome/Dockerfile index 3c03b0ae..1e6153fa 100644 --- a/node-playwright-chrome/Dockerfile +++ b/node-playwright-chrome/Dockerfile @@ -96,7 +96,13 @@ RUN apt update \ && apt clean -y && apt autoremove -y \ && rm -rf /root/.npm \ # This is needed to remove an annoying error message when running headful. - && mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix + && mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix \ + \ + # Point the default per-user npm cache dirs (~/.npm) at the shared cache, so tooling + # that bypasses NPM_CONFIG_CACHE - and templates that `rm -r ~/.npm` - keep working. + && rm -rf /root/.npm /home/myuser/.npm \ + && ln -s /pkg-cache/npm /root/.npm \ + && ln -s /pkg-cache/npm /home/myuser/.npm # Run everything after as non-privileged user. USER myuser diff --git a/node-playwright-firefox/Dockerfile b/node-playwright-firefox/Dockerfile index f2eacaf2..4a61631b 100644 --- a/node-playwright-firefox/Dockerfile +++ b/node-playwright-firefox/Dockerfile @@ -86,7 +86,13 @@ RUN apt update \ && apt clean -y && apt autoremove -y \ && rm -rf /root/.npm \ # This is needed to remove an annoying error message when running headful. - && mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix + && mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix \ + \ + # Point the default per-user npm cache dirs (~/.npm) at the shared cache, so tooling + # that bypasses NPM_CONFIG_CACHE - and templates that `rm -r ~/.npm` - keep working. + && rm -rf /root/.npm /home/myuser/.npm \ + && ln -s /pkg-cache/npm /root/.npm \ + && ln -s /pkg-cache/npm /home/myuser/.npm # Run everything after as non-privileged user. USER myuser diff --git a/node-playwright-webkit/Dockerfile b/node-playwright-webkit/Dockerfile index 2ea05569..3c26642c 100644 --- a/node-playwright-webkit/Dockerfile +++ b/node-playwright-webkit/Dockerfile @@ -75,7 +75,13 @@ RUN apt update \ && apt clean -y && apt autoremove -y \ && rm -rf /root/.npm \ # This is needed to remove an annoying error message when running headful. - && mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix + && mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix \ + \ + # Point the default per-user npm cache dirs (~/.npm) at the shared cache, so tooling + # that bypasses NPM_CONFIG_CACHE - and templates that `rm -r ~/.npm` - keep working. + && rm -rf /root/.npm /home/myuser/.npm \ + && ln -s /pkg-cache/npm /root/.npm \ + && ln -s /pkg-cache/npm /home/myuser/.npm # Run everything after as non-privileged user. USER myuser diff --git a/node-playwright/Dockerfile b/node-playwright/Dockerfile index d80d2aff..c9932b22 100644 --- a/node-playwright/Dockerfile +++ b/node-playwright/Dockerfile @@ -98,7 +98,13 @@ RUN apt update \ && apt clean -y && apt autoremove -y \ && rm -rf /root/.npm \ # This is needed to remove an annoying error message when running headful. - && mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix + && mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix \ + \ + # Point the default per-user npm cache dirs (~/.npm) at the shared cache, so tooling + # that bypasses NPM_CONFIG_CACHE - and templates that `rm -r ~/.npm` - keep working. + && rm -rf /root/.npm /home/myuser/.npm \ + && ln -s /pkg-cache/npm /root/.npm \ + && ln -s /pkg-cache/npm /home/myuser/.npm # Run everything after as non-privileged user. USER myuser diff --git a/node-puppeteer-chrome/Dockerfile b/node-puppeteer-chrome/Dockerfile index a291240a..4df38202 100644 --- a/node-puppeteer-chrome/Dockerfile +++ b/node-puppeteer-chrome/Dockerfile @@ -105,7 +105,13 @@ RUN apt update \ && apt clean -y && apt autoremove -y \ && rm -rf /root/.npm \ # This is needed to remove an annoying error message when running headful. - && mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix + && mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix \ + \ + # Point the default per-user npm cache dirs (~/.npm) at the shared cache, so tooling + # that bypasses NPM_CONFIG_CACHE - and templates that `rm -r ~/.npm` - keep working. + && rm -rf /root/.npm /home/myuser/.npm \ + && ln -s /pkg-cache/npm /root/.npm \ + && ln -s /pkg-cache/npm /home/myuser/.npm # Run everything after as non-privileged user. USER myuser diff --git a/node/Dockerfile b/node/Dockerfile index b1009c18..40a34ccf 100644 --- a/node/Dockerfile +++ b/node/Dockerfile @@ -23,7 +23,13 @@ RUN npm config --global set update-notifier false \ # Pre-create the shared package-manager cache dirs (see the *_CACHE_*/*_STORE_DIR env vars), world-writable so any # user can populate them and `rm -rf` them at the end to reclaim space, regardless of which user the Actor runs as. && mkdir -p /pkg-cache/npm /pkg-cache/yarn /pkg-cache/pnpm/store /pkg-cache/pnpm/cache \ - && chmod -R 777 /pkg-cache + && chmod -R 777 /pkg-cache \ + \ + # Point the default per-user npm cache dirs (~/.npm) at the shared cache, so tooling + # that bypasses NPM_CONFIG_CACHE - and templates that `rm -r ~/.npm` - keep working. + && rm -rf /root/.npm /home/myuser/.npm \ + && ln -s /pkg-cache/npm /root/.npm \ + && ln -s /pkg-cache/npm /home/myuser/.npm WORKDIR /usr/src/app