commit 8941c1e696360fd4a10a1dfeb107e8955f518350
parent e4bc59181ffa629a0c7c6d4fb36383af208a3398
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Mon, 28 Sep 2026 12:47:01 -0400
docker: Dockerfile.build on Node 22.23.2 and pnpm 11.26.0, and its comments say the network is on
The image installed with pnpm 9.15.4 on node:20, which does not know the
workspace's `allowBuilds` / `minimumReleaseAgeExclude`. It now pins what the
workspace runs on (node:22.23.2-bookworm-slim — bookworm like the root
Dockerfile's build stage — and pnpm 11.26.0, which needs Node >= 22.13), as
build args with those defaults.
pnpm 11 checks before every `pnpm exec` / `pnpm run` that the whole workspace
is installed and runs `pnpm install` when it is not. The image installs only
common and export, so after `COPY . .` it never is, and that install fails as
the container's non-root uid (EACCES on /repo) — the first `pnpm --filter …
exec` in build-site.sh would never start. `pnpm_config_verify_deps_before_run
=false` turns the check off (the deps are frozen at image build), and the
update notice goes with it.
Three comments said the per-site containers run with --network=none; they do
not (runDockerBuildOne leaves it on for next/font/google). They now say so, and
the corepack reason no longer rests on being offline.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
1 file changed, 35 insertions(+), 13 deletions(-)
diff --git a/Dockerfile.build b/Dockerfile.build
@@ -1,13 +1,24 @@
# Build image for the docker export pipeline (Build all, whenever a container engine answers).
#
-# Bakes the repo source + installed deps so each per-site build container is
-# hermetic and reproducible. The corpus, the shared LMDB index, the .export-index
+# Bakes the repo source + installed deps so every per-site build container runs
+# the same toolchain and code. The corpus, the shared LMDB index, the .export-index
# staging, and the archive cache are bind-mounted READ-ONLY at run time — never
# baked (they're hundreds of GB and change constantly). Each container writes only
# its per-site output mount (/site). Deploy never runs here; it stays on the host.
+# The network is left ON at run time (common/publish/build.ts, runDockerBuildOne):
+# `next build` fetches each site's fonts through next/font/google.
#
-# Entry: docker/build-site.sh runs `compose:site + next build` for one SITE_ID.
-FROM node:20-bookworm-slim
+# Entry: docker/build-site.sh runs `archilyzer build site <SITE_ID> --nodata`
+# (compose + next build) for one SITE_ID.
+#
+# Node and pnpm are pinned to what the workspace runs on. pnpm 11 is not optional:
+# pnpm-workspace.yaml's `allowBuilds` and `minimumReleaseAgeExclude` are keys
+# pnpm 9 does not know, and pnpm 11 itself needs Node >= 22.13. bookworm, like
+# the root Dockerfile's build stage.
+ARG NODE_IMAGE=node:22.23.2-bookworm-slim
+FROM ${NODE_IMAGE}
+
+ARG PNPM_VERSION=11.26.0
# Archive compressors the export build shells out to. `zip` is the default format;
# `tar`/`xz`/`gzip` cover the other configurable archive formats.
@@ -15,30 +26,41 @@ RUN apt-get update \
&& apt-get install -y --no-install-recommends zip tar xz-utils gzip \
&& rm -rf /var/lib/apt/lists/*
-# Install pnpm as a plain global binary (NOT corepack): the fan-out containers run
-# fully offline (--network=none) as an arbitrary host uid with HOME=/tmp, where
-# corepack — lacking a packageManager pin — would try to fetch pnpm from the
-# registry and fail. A global install needs no network at run time.
-RUN npm install -g pnpm@9.15.4
+# Install pnpm as a plain global binary (NOT corepack). There is no packageManager
+# pin in package.json, so corepack would download a pnpm of its own choosing at
+# run time — in every container, since each runs as an arbitrary host uid with
+# HOME=/tmp and keeps nothing between runs. A global install is baked once and
+# needs nothing at run time.
+RUN npm install -g "pnpm@${PNPM_VERSION}"
WORKDIR /repo
# Install deps first for layer caching — rebuilds only when a manifest or the
-# lockfile moves. `onlyBuiltDependencies` in pnpm-workspace.yaml rebuilds the
-# native modules (lmdb, msgpackr-extract, esbuild).
+# lockfile moves. `allowBuilds` in pnpm-workspace.yaml rebuilds the native
+# modules (lmdb, msgpackr-extract, esbuild) for this image.
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
COPY common/package.json common/package.json
COPY export/package.json export/package.json
RUN pnpm install --frozen-lockfile
+# Only common and export (and the root) are installed — all the export build
+# needs. Before every `pnpm exec` / `pnpm run`, pnpm 11 checks that the WHOLE
+# workspace is installed and runs `pnpm install` when it is not; after `COPY . .`
+# below it never is, and that install fails as the non-root runtime uid
+# (EACCES on /repo). The deps are frozen here, so the check is off. So is the
+# update notice, which every site's log would otherwise print.
+ENV pnpm_config_verify_deps_before_run=false \
+ pnpm_config_update_notifier=false
+
# Bake source last so a code change only re-runs from here.
COPY . .
# Containers run with `-u <host-uid>` (so /site outputs are host-owned, not root).
# Next writes a couple of fixed-location files into the export package dir
# (next-env.d.ts, tsconfig.tsbuildinfo) and the entrypoint symlinks .next/out from
-# there — so that one dir must be writable by an arbitrary runtime uid. The image
-# is ephemeral and isolated (--network=none), so widening it here is harmless.
+# there — so that one dir must be writable by an arbitrary runtime uid. Every
+# container is ephemeral (`docker run --rm`) and writes nothing back but its /site
+# mount, so widening it here is harmless.
RUN chmod -R a+rwX /repo/export
ENTRYPOINT ["bash", "docker/build-site.sh"]