Skip to content

Split the Docker build into build, deps, and runtime stages - #2422

Merged
kmcginnes merged 2 commits into
mainfrom
docker-stages
Oct 16, 2026
Merged

kmcginnes merged 2 commits into
mainfrom
docker-stages

Conversation

@kmcginnes

@kmcginnes kmcginnes commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

The image was built in one stage. It installed everything, built, deleted node_modules, reinstalled production dependencies in place, then stripped npm, pnpm and corepack back out. Splitting it into stages gives the same image without the cleanup pass, and production dependencies get a layer that is reused until a manifest changes. Both build stages and the runtime stay on the same pinned Amazon Linux 2023 snapshot.

os          amazonlinux snapshot, NODE_VERSION
├── toolchain   + Node.js, corepack, pnpm
│   ├── build   pnpm install, pnpm build, drop node_modules
│   └── deps    manifests only, pnpm install --prod
└── runtime     + openssl, minus python3/dnf/yum
                node binary and its LICENSE from toolchain
                node_modules from deps, then the app tree from build

The runtime never gets npm, pnpm or corepack, so nothing has to be uninstalled. test_build_docker.yml now also starts the image in notebook mode on 9250 and over HTTPS on 443, in addition to the plain HTTP run on 80.

Evidence

Old and new images were each built from a clean checkout of main and compared:

  • Config: ExposedPorts, Entrypoint, WorkingDir, User, PATH, NODE_VERSION and HOME are identical. VP_GIT_HOOKS now lives only in the build stage, since nothing runs git at runtime.
  • Files: /graph-explorer has the same 41,681 node_modules entries. The app tree matches too, except for 4 npm log files the old image carried in .npm/.
  • Size: 875 MB, down from 984 MB.
  • Runs: both images answer /status over HTTP on 80, in notebook mode on 9250, and over HTTPS on 443.
  • Upgrades: state written by the published 3.3.0 image works unchanged on the new one. A mounted CONFIGURATION_FOLDER_PATH folder with a user .env starts and serves the same /defaultConnection. A cert-info named volume serves the same certificate, matched by SHA-256 fingerprint, without HOST.
  • Config and certs: against the current main image, a mounted config.json (read-write and read-only) produces identical .env, defaultConnection.json and /defaultConnection. A config folder ends up with identical contents, and bind-mounted user certificates are served the same way.
  • Checks: Trivy reports no HIGH or CRITICAL findings on either image. In the new image, node --version is v24.21.0, openssl is present, and npm, pnpm, corepack, python3, yum and dnf are absent.

Merge Danger

Door: two-way

Impact: Docker image

The published image has the same contents and entrypoint, so users and the notebook setup see no change. A failure would show up at container start, which the 3 /status runs check.

Related

@kmcginnes kmcginnes left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved

@kmcginnes
kmcginnes merged commit f40ac00 into main Oct 16, 2026
6 checks passed
@kmcginnes
kmcginnes deleted the docker-stages branch October 16, 2026 16:44
kmcginnes added a commit that referenced this pull request Oct 18, 2026
## Summary

The Dockerfile pinned Node.js a second time in `ENV NODE_VERSION`,
downloaded it with `curl | tar` and no checksum, and installed pnpm
through a pinned Corepack. The build and deps stages now run in the
official Vite+ image. It installs the Node.js in `.node-version` and the
pnpm in `packageManager`, verifies both, and the runtime stays on the
same Amazon Linux 2023 snapshot.

```diff
-FROM amazonlinux:2023.… AS os
-ENV NODE_VERSION=24.21.0
-FROM os AS toolchain            # curl nodejs.org | tar, npm i -g corepack@0.36.0
+FROM ghcr.io/voidzero-dev/vite-plus:1.1.0@sha256:… AS toolchain
 FROM toolchain AS build          vp install, vp run build, export node + LICENSE
 FROM toolchain AS deps           vp install --prod
-FROM os AS runtime
+FROM amazonlinux:2023.… AS runtime
```

`.node-version` is now the only Node.js pin the image uses.
`test_build_docker.yml` checks that the image's `node --version` matches
it. `docs/development.md` and the Dependabot comments drop the Corepack
and `NODE_VERSION` notes.

## Evidence

**Before:** the image's Node.js came from `ENV NODE_VERSION`, with no
signature or checksum check, and pnpm came from Corepack 0.36.0.
**After:** the image builds with `Done in 15.7s using pnpm v12.8.1`. The
new CI step reports `✓ node v24.21.0`, which matches `.node-version`.

Compared with an image built from current `main`:

- **Contents:** every file under `/graph-explorer` and `/usr/local` is
byte-identical, with the same owners and modes. The only exceptions are
`Dockerfile` and pnpm's install metadata. `/usr/local/bin/node` has the
same SHA-256, so Vite+ fetched the same official build.
- **Config:** `ExposedPorts`, `Entrypoint`, `WorkingDir`, `User`, `PATH`
and `HOME` are unchanged. `NODE_VERSION` is gone from the image
environment.
- **Size:** 875 MB, the same as before.
- **Trivy:** no HIGH or CRITICAL findings.

**Verified live:** I ran both images through every documented deployment
path and compared the results:

- **Startup:** both answer `/status` over HTTP on 80, in notebook mode
on 9250, and over HTTPS on 443.
- **Config:** a mounted `config.json`, read-write and read-only, gives
identical `.env`, `defaultConnection.json` and `/defaultConnection`.
- **Config folder:** a `CONFIGURATION_FOLDER_PATH` folder with a user
`.env` ends up with identical contents.
- **Certificates:** bind-mounted user certificates are served the same
way, and a restart keeps the same certificate.
- **Upgrade from 3.3.0:** a config folder and a `cert-info` named volume
written by the published 3.3.0 image work unchanged on the new image.
The volume serves the same certificate fingerprint.
- **Stop:** `docker stop` takes 0.13s and exits 0.

## Merge Danger

**Door:** two-way

**Impact:** Docker image

The shipped image has the same contents and behavior. One environment
variable disappears: `NODE_VERSION` is no longer set inside the
container. Nothing in this repo reads it, and it was only ever set to
report the build's Node.js version. Builds now pull
`ghcr.io/voidzero-dev/vite-plus`, pinned by digest, in addition to the
Amazon Linux image.

## Related

- Related to #2305
- Related to #2422
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant