Skip to main content
[ PIER ]

Self-hosted npm registry — Pier

Private packages and a transparent npmjs.org mirror in one Rust binary. Works with npm 7–11, yarn classic, yarn berry 2/3/4, pnpm and bun — no Verdaccio container required.

Private + proxy mode in one binary

Publish scoped or unscoped private packages and transparently mirror registry.npmjs.org (or any compatible upstream) from the same /registry/npm/ endpoint. Private publishes always win over proxy entries — no scope routing needed in .npmrc.

Works with every modern client

npm 7–11, yarn classic 1.22, yarn berry 2/3/4, pnpm 9/10 and bun. All authentication, dist-tags, publish, install, deprecate and unpublish flows are end-to-end tested against each client in the matrix.

Lazy tarball cache with LRU eviction

Upstream packuments are cached and revalidated through If-None-Match (304 short-circuit). Tarballs are pulled lazily on first install and a background LRU GC keeps the on-disk cache under a configurable size cap so the disk never fills silently.

Streamed publish and download

Tarballs stream through axum::Body::from_stream — Pier never buffers a 200 MiB tarball in RAM. Hot-path serving uses tokio_util::ReaderStream so memory stays bounded regardless of package size.

Web login flow (--auth-type=web)

npm login --auth-type=web opens the browser, the user authenticates in the panel with 2FA, and a long-lived pier_npm_ token lands in .npmrc — no manual token copy-paste.

Pin, audit and inspect from the UI

Mirror tab lists every cached public package with version count and on-disk size. Star packages your team cares about and filter the rest out. Each row links to a detail page with dist-tags, versions, and a Download-latest button.

Connect npm to your Pier registry

  1. 01

    Mint a token in the panel

    Open Packages → Manage tokens → New token. Pier returns a pier_npm_… string shown once — copy it before closing the modal.

  2. 02

    Drop a .npmrc into your project

    registry=https://your-pier-host/registry/npm/, then //your-pier-host/registry/npm/:_authToken=pier_npm_…, plus always-auth=true so yarn 1 sends the bearer on GET.

  3. 03

    Optionally enable upstream proxy

    Packages → Upstream proxy → toggle Enable, default upstream is registry.npmjs.org. Set the max cache size (0 = unlimited) and TTL for packument revalidation.

  4. 04

    Publish or install

    npm publish for a private package, or npm install <any-public-pkg> to fill the proxy cache. The whole team uses the same one URL — no scope routing.

Common questions

Can Pier replace Verdaccio?

For private publish + proxy mirror, yes — Pier supports the same npm registry HTTP protocol, plus dist-tag/deprecate/unpublish CLIs and the web-login flow. Not yet on parity: npm search, npm owner / RBAC, and audit endpoints — those are tracked on the roadmap. Everything else works against an automated client matrix (npm/yarn1/yarn2/yarn3/yarn4/pnpm/bun).

Which npm clients does Pier support?

npm 7 through 11, yarn classic 1.22, yarn berry 2/3/4 (with npmAlwaysAuth: true in .yarnrc.yml), pnpm 9 and 10, and bun latest. Each client is exercised against a real Pier in CI as part of pier-tests, so behavior is locked in across upgrades.

How is the upstream proxy mode different from a CDN cache?

Pier caches the metadata layer (packuments) forever with TTL-based revalidation, and the tarball layer lazily — only versions a client actually installed land on disk. A CDN caches HTTP responses opaquely; Pier understands the npm protocol, so it can serve abbreviated packuments, return 304 on If-None-Match, and rewrite dist.tarball URLs so installs always go through your host.

Does Pier work with yarn 4 (yarn berry)?

Yes — yarn 2/3/4 use .yarnrc.yml instead of .npmrc and require npmAlwaysAuth: true to send the bearer on GET. With that one line of config the full install/publish flow works against Pier; the docs page for yarn berry walks through the exact yarnrc file.

Can I publish private packages and proxy public ones at the same time?

Yes — private and proxy entries live in the same /registry/npm/ endpoint. Private publishes always shadow proxy entries with the same name, so you can keep an internal @your-org/* scope and still install left-pad or next through the same URL.

How much disk does the cache need?

Packuments are tiny (each package ≈ 1–50 KB of JSON). Tarballs dominate: a single npm install next pulls roughly 100 MiB across transitive deps. For a personal dev box, 500 MiB is comfortable; for a small team 2–5 GiB; for shared CI mirrors 20+ GiB. Set Max cache size to 0 for unlimited.

Does it support 2FA for npm publish?

Tokens are minted in the panel where 2FA is enforced on the operator session — so the npm login --auth-type=web flow naturally inherits 2FA. The legacy npm login CouchDB flow does not prompt for OTP today; we recommend disabling it via panel settings if you require 2FA on every token mint.

Is there a CI integration?

Use a long-lived token (Packages → Manage tokens) and write the same .npmrc in your CI step. For GitHub Actions and GitLab CI there are ready snippets in the docs CI page that work for any of the supported clients.

Related

Ready to deploy?

One command installs Pier on any Ubuntu or Debian VPS.

curl -fsSL https://pier.sh/install | sudo bash