Files
pnpm/pnpr/docker
Zoltan Kochan e2dab007f8 ci(pnpr): publish a Docker image on release (#12619)
* ci(pnpr): publish a Docker image on release

Add a `docker` job to the pnpr release workflow that builds and pushes a
multi-arch (linux/amd64, linux/arm64) image to ghcr.io/pnpm/pnpr after the
npm packages are published.

The image is built from the static musl binaries the release job already
produces, so no Rust toolchain runs in the Docker build and the image
matches exactly what ships to npm. It is based on debian:stable-slim with
ca-certificates (pnpr reaches upstream registries over HTTPS via rustls'
platform cert verifier), runs as a non-root user, declares storage and
cache volumes, exposes 7677, and defaults to binding 0.0.0.0 so the server
is reachable from outside the container. The build self-checks that
`pnpr --version` matches the PNPR_VERSION build-arg.

Stable releases also move the mutable `latest` tag; prereleases (versions
containing a hyphen) only get the exact-version tag.

* ci(pnpr): drop unused id-token permission from docker job

The docker job uses only docker/build-push-action's built-in provenance
and SBOM attestations, which are pushed to GHCR via GITHUB_TOKEN. It does
not call actions/attest-build-provenance, so the OIDC id-token: write
permission was unused. Drop it to follow least privilege.

* ci(pnpr): verify staged binary checksum in the docker image build

The build job now pins each binary's SHA256 at build time and uploads it
with the artifact. The docker job verifies the staged binaries against
those checksums and passes them as build-args, and the Dockerfile
re-verifies the binary it copies before trusting it. A 'pnpr --version'
check only confirms reported metadata, so it cannot stand in for an
integrity check on the binary itself. Mirrors the existing pnpm image.

* ci(pnpr): extract release artifacts into an isolated directory

Extract the downloaded musl tarballs into a throwaway directory and move
only the expected, checksum-verified regular files into the Docker build
context. A malformed archive (path traversal or symlink entries) can no
longer escape into the workspace and overwrite the Dockerfile or staged
binaries before the image is pushed to GHCR.

* docs(pnpr): add a language to the docker README code fence

The image-name fenced block lacked a language identifier, tripping
markdownlint MD040. Tag it as text.
2026-06-24 14:40:29 +00:00
..

pnpr Docker image

Official image for pnpr, the pnpm-compatible npm registry server, published to GitHub Container Registry.

ghcr.io/pnpm/pnpr

Based on debian:stable-slim with the standalone pnpr binary (static musl build, the same artifact published to npm). The container runs as a non-root pnpr user and listens on port 7677.

Tags

Tag Meaning
<version> Exact, immutable (e.g. 0.2.3). Includes prereleases.
latest Most recent stable release. Not updated for prereleases.

Supported platforms

linux/amd64, linux/arm64.

Usage

docker run --rm -p 7677:7677 \
  -v pnpr-storage:/pnpr/storage \
  ghcr.io/pnpm/pnpr:latest

The default command binds to 0.0.0.0:7677 and stores published packages in /pnpr/storage and the disposable upstream mirror in /pnpr/cache (both declared as volumes). To use a custom config, mount it and point pnpr at it:

docker run --rm -p 7677:7677 \
  -v "$PWD/config.yaml:/pnpr/config.yaml:ro" \
  -v pnpr-storage:/pnpr/storage \
  ghcr.io/pnpm/pnpr:latest --listen 0.0.0.0:7677 --config /pnpr/config.yaml

Then point pnpm at it:

pnpm config set registry http://localhost:7677

Build locally

The build context expects the binary for each target architecture, staged as pnpr-amd64 / pnpr-arm64. Build one with cross (or cargo for the host arch) and drop it in:

The build verifies the binary against a SHA256 checksum before trusting it, so pass the checksum for the architecture you're building:

VERSION=0.2.3
cross build -p pnpr --bin pnpr --release --target x86_64-unknown-linux-musl
cp target/x86_64-unknown-linux-musl/release/pnpr pnpr/docker/pnpr-amd64
docker buildx build \
  --build-arg PNPR_VERSION=${VERSION} \
  --build-arg PNPR_SHA256_AMD64=$(shasum -a 256 pnpr/docker/pnpr-amd64 | awk '{print $1}') \
  --platform linux/amd64 \
  --load \
  -t pnpr-test ./pnpr/docker
docker run --rm pnpr-test --version

Release

Images are built and pushed by the docker job in .github/workflows/pnpr-release-to-npm.yml, which runs after the npm packages are published. The build verifies each staged binary against the SHA256 checksum pinned by the release job and fails if pnpr --version in the image doesn't match the PNPR_VERSION build-arg.