Spike: how the container image should acquire Node and pnpm
- Dominant language
- TypeScript
- Stars
- 481
- Forks
- 108
- Avg merge
- 6d 8h
- Merged PRs (30d)
- 5
Description
## Goal
How should the container image acquire Node and pnpm? The current approach in the `Dockerfile` fetches both from the public internet at build time, outside every guardrail the repo applies to its other dependencies.
```dockerfile
curl -fsSL https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-${NODE_ARCH}.tar.xz | tar -xJ -C /usr/local --strip-components=1 && \
npm install --global corepack@latest && \
```
Two concrete gaps. The Node tarball is piped straight into `tar` with no checksum check against the published `SHASUMS256.txt`, so whatever the CDN returns is what gets extracted and run. And `corepack@latest` is an unpinned floating version resolved at build time, which sits outside the lockfile and therefore outside `minimumReleaseAge: 1440`, `trustPolicy: no-downgrade`, and `blockExoticSubdeps: true` — settings the repo applies to everything else it installs. A build today and a build next week can produce different images from the same commit.
There's a related maintenance cost: the Node version is pinned in five places (`Dockerfile` `NODE_VERSION`, `.nvmrc`, `engines.node` in two manifests, `devEngines.runtime.version`) that have to be moved together, plus the requirements in `docs/development.md`.
## Approaches to investigate
- **Node as a package dependency.** Declaring Node in `package.json` would put the runtime under the lockfile and the release-age gate, and would decouple the image from whatever Node the build machine happens to have. Worth checking what this actually looks like in practice — which package provides it, how it interacts with `devEngines`, whether it collapses several of the five pins into one, and what it does to image size.
- **Install pnpm without a separate Node bootstrap.** pnpm publishes standalone binaries that bundle their own Node, so the image may be able to stay entirely within the pnpm world rather than fetching Node and then layering corepack on top. Investigate whether this can replace both the tarball fetch and the corepack install, and how it squares with the `packageManager` field.
- **Keep the current shape and harden it.** Verify the tarball against `SHASUMS256.txt` before extracting, and pin corepack to an exact version. Lowest-risk option, and the baseline the others should be compared against.
- **A prebuilt Node base image.** Using an official Node image (or a distroless variant) instead of Amazon Linux plus a manual Node install. Note the current base image choice is deliberate, so this needs to weigh what would be lost.
Also worth confirming: `/usr/local/lib/node_modules/corepack` currently survives into the final image even though the bin shims are removed, so a dead copy ships either way.
## Expected Outcome
A recommendation with enough detail to task out, covering: which approach we take, what it does to image size and build reproducibility, whether it reduces the number of places the Node version is declared, and whether any of it changes the contributor setup documented in `docs/development.md`. A proof-of-concept `Dockerfile` for the preferred option would make the recommendation much easier to act on.
## Related Issues
- Related to #2153
> [!IMPORTANT]
> Internal only — this issue is maintained by the core team and is not accepting external contributions.
Contributor guide
Research direction
Start with the Dockerfile, the two manifests containing engines.node, .nvmrc, devEngines.runtime.version, packageManager, and docs/development.md; trace how Node and pnpm are acquired and where their versions are pinned. Compare the listed approaches, including image size and reproducibility, then document a recommendation, its effect on contributor setup and version pins, and a proof-of-concept Dockerfile for the preferred option.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, node.js
- Domain
- build-system, devops, tooling
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100