Docker
Vite+ publishes an official Docker image with the vp CLI preinstalled:
ghcr.io/voidzero-dev/vite-plusUse it for builds, CI, and devcontainers. It is not intended as a production runtime image.
vp resolves the Node.js version from your project (.node-version, devEngines.runtime, or engines.node) and downloads that exact version during install/build. This means the image does not need Node-version-specific tags.
For production, use a multi-stage build: build the app with the Vite+ image, then copy only the resolved Node.js binary, build output, and production dependencies into a smaller runtime image.
Image tags
Tags track the vp version:
| Tag | Meaning |
|---|---|
ghcr.io/voidzero-dev/vite-plus:latest | Latest release |
ghcr.io/voidzero-dev/vite-plus:<major> | Latest major |
ghcr.io/voidzero-dev/vite-plus:<major>.<minor> | Latest minor |
ghcr.io/voidzero-dev/vite-plus:<major>.<minor>.<patch> | Exact version |
The examples use :latest to track the newest release; pin an exact tag or a digest if you need reproducible builds. The image is published for linux/amd64 and linux/arm64 and runs as a non-root user by default.
Browse all published versions and digests on the GitHub package page.
Production: SSR / Node.js server app
For apps that run Node.js in production (SvelteKit, Nuxt, a custom Vite SSR server, and so on), build with the toolchain image and copy the resolved Node.js and the built app into a slim runtime stage:
# syntax=docker/dockerfile:1
# --- build stage: the official Vite+ toolchain image ---
FROM ghcr.io/voidzero-dev/vite-plus:latest AS build
WORKDIR /app
# Install dependencies first so this layer is cached across source changes.
COPY --chown=vp:vp package.json pnpm-lock.yaml .node-version* ./
RUN vp install --frozen-lockfile
# Build. vp reads .node-version and provisions that exact Node.js automatically.
COPY --chown=vp:vp . .
RUN vp build
# Export the exact resolved Node.js binary for the runtime stage.
RUN cp "$(vp env which node | head -1)" /tmp/node
# --- deps stage: production-only dependencies ---
# A separate, fresh `--prod` install so devDependencies (including the vite-plus
# toolchain) are excluded. Running `--prod` over the full install above would not
# prune the already-installed devDependencies.
FROM ghcr.io/voidzero-dev/vite-plus:latest AS deps
WORKDIR /app
COPY --chown=vp:vp package.json pnpm-lock.yaml .node-version* ./
RUN vp install --frozen-lockfile --prod
# --- runtime stage: small, glibc, no vp ---
FROM debian:bookworm-slim AS runtime
WORKDIR /app
ENV NODE_ENV=production
# The exact Node.js from .node-version (official, signature-verified build).
COPY --from=build /tmp/node /usr/local/bin/node
COPY --from=build /app/dist ./dist
COPY --from=deps /app/node_modules ./node_modules
COPY --from=build /app/package.json ./
USER nobody
EXPOSE 3000
CMD ["node", "dist/server.js"]The deployed image contains only Node.js plus your app and production dependencies, and matches .node-version exactly. It is much smaller than the default node:* image; see the distroless tip below for the smallest result.
Prune production dependencies in a separate stage
Install production dependencies in their own deps stage as shown. Running vp install --prod after a full vp install in the same stage does not remove the already-installed devDependencies, so the vite-plus toolchain would be copied into the runtime image. If your server bundle is fully self-contained (no un-bundled runtime dependencies), you can skip copying node_modules entirely.
Smaller still
For a shell-less, minimal-CVE runtime, swap the runtime base for distroless (gcr.io/distroless/cc) and keep an ENTRYPOINT in vector form. It is glibc based, so the copied Node.js binary remains compatible.
Production: static SPA / SSG
A static site needs no Node.js at runtime; serve the build output with any static server:
FROM ghcr.io/voidzero-dev/vite-plus:latest AS build
WORKDIR /app
COPY --chown=vp:vp package.json pnpm-lock.yaml .node-version* ./
RUN vp install --frozen-lockfile
COPY --chown=vp:vp . .
RUN vp build
FROM nginx:alpine AS runtime
COPY --from=build /app/dist /usr/share/nginx/htmlContinuous integration
Use the image directly in container-based CI (GitLab CI, Buildkite, CircleCI, Jenkins, and others):
build:
image: ghcr.io/voidzero-dev/vite-plus:latest
script:
- vp install --frozen-lockfile
- vp check
- vp test
- vp buildOn GitHub Actions, prefer setup-vp instead of the image.
Devcontainers
Use the image as a ready-to-go development container with the toolchain preinstalled:
{
"image": "ghcr.io/voidzero-dev/vite-plus:latest",
}Ad-hoc usage
Run any vp command against a project without installing vp on your machine:
docker run --rm -it -v "$PWD:/app" -w /app ghcr.io/voidzero-dev/vite-plus vp buildNotes
- Node.js version: provisioned from
.node-version,engines.node, ordevEngines.runtimeat build time, so there is no Node.js-specific image tag. The dependencyCOPYuses a.node-version*glob so the file is optional: projects that pin viaengines.node/devEngines.runtimeneed no.node-version, and those that use one have it available in every stage. - Non-root user: the image runs as the non-root
vpuser, so copy sources withCOPY --chown=vp:vp ...as shown. Without it,COPYwrites root-owned files thatvp installcannot update (permission denied). - Native addons: the image includes a C/C++ build toolchain (
build-essential,python3), so native dependencies such asbetter-sqlite3compile duringvp install. - glibc: the image is glibc based so it uses the official, signature-verified Node.js builds.
- Custom base image: to add
vpto your own base image instead, run the installer:curl -fsSL https://vite.plus | bash(setVP_VERSIONto pin a version).