Verdaccio

Lightweight private npm proxy registry

Developer Tools & Git ★ 17.9k stars Easy setup MIT

Verdaccio is a lightweight private npm registry and proxy that caches packages and hosts private modules. It targets JavaScript teams who need a private package registry. It is deployed via Docker or npm.

Key features

  • Private npm registry
  • Caching proxy for npmjs
  • Pluggable authentication
  • Zero-config startup

Pros & cons

Strengths

  • Very easy to start
  • Lightweight
  • Good for private packages

Trade-offs

  • npm ecosystem only
  • Limited UI

Verdaccio replaces

Last reviewed Aug 26, 2026 · 796 words

Sixty seconds. That is roughly how long it takes to go from nothing to a working private npm registry with npx verdaccio, and it is the whole pitch: Verdaccio is the registry you run when the alternative is a two-hour Nexus install or paying per-seat for a hosted one. It proxies and caches npmjs.org for everything public, hosts your @yourorg/* packages privately, and does both on 256 MB of RAM. The setup is trivial. The three config decisions below are where people get it wrong.

Docker in five lines, then the config file does the work

services:
  verdaccio:
    image: verdaccio/verdaccio:6
    ports:
      - "4873:4873"
    volumes:
      - ./storage:/verdaccio/storage
      - ./conf:/verdaccio/conf
    restart: unless-stopped

Port 4873 is the default. The volume that matters is storage, which holds every published tarball and every cached upstream package, and conf/config.yaml, which is the entire behaviour of the server. Point your clients at it:

npm set registry http://localhost:4873/
npm adduser --registry http://localhost:4873/

From that moment every npm install goes through Verdaccio. Public packages are fetched from npmjs on first request and served from disk after, so a second npm ci of the same lockfile on the same network no longer touches the internet. On a CI runner that alone is worth the container.

Decision 1: close registration on day one

The default config.yaml allows anyone who can reach the port to npm adduser. That is fine on a laptop and a problem on a server. After creating your accounts, set:

auth:
  htpasswd:
    file: ./htpasswd
    max_users: -1

max_users: -1 disables new signups entirely; existing users in the htpasswd file keep working. For a team of more than a handful, the pluggable auth is the feature: there are plugins for LDAP, GitHub OAuth, and generic OIDC, and it is a single config block to swap. For a homelab, htpasswd and a closed door is plenty.

Decision 2: scope who can publish what

The packages section is an ordered list of glob rules, and the default is looser than you want:

packages:
  '@myorg/*':
    access: $authenticated
    publish: $authenticated
  '**':
    access: $all
    publish: $nobody
    proxy: npmjs

Read it top down: your scope is readable and publishable by logged-in users; everything else is readable by anyone, publishable by nobody, and fetched from the npmjs uplink. That last line is what stops a typo'd npm publish from planting a package called lodash in your private registry that shadows the real one for everyone on the network. This is the classic dependency-confusion footgun and the publish: $nobody on the catch-all rule is the fix.

Decision 3: decide what survives a disk failure

Cached public packages are disposable; they come back from npmjs on demand. Private packages are not, and Verdaccio has no built-in backup. The storage directory is plain files, so an rsync or a restic job that includes it is the whole answer, but it has to actually be in the job. The 3-2-1 backups guide covers a rotation that would catch this. If you would rather not think about it, the S3 and Google Cloud storage plugins move the tarballs to a bucket and leave the container stateless.

When you do not need Verdaccio at all

If you already run Gitea or Forgejo, they have a package registry built in that speaks npm (and PyPI, Maven, containers and more), authenticated with the same accounts and permissions you already manage. It does not proxy-cache npmjs, so npm install of public packages still goes out to the internet, but for "publish our 6 private packages somewhere" it is zero extra services. GitLab does the same. Verdaccio wins when you want the cache, when you need per-package publish rules finer than a repo's permissions, or when the registry has to stand alone. It stays firmly in the dev-tools category as a JavaScript-only tool; for container images the equivalent single-purpose registry is Harbor.

The web UI is the real weak spot in the catalogue's list of cons: it lists packages and shows READMEs, and that is about it. Everything operational happens in the YAML file.

What I'd do

Verdaccio 6 in Docker behind Caddy with HTTPS, max_users: -1 after the first adduser, the three-rule packages block above with publish: $nobody on the catch-all, and the storage directory in the nightly backup. Set it as the registry in every CI runner's .npmrc and watch the install times drop. If you are already on Gitea and only need to publish, use its registry instead and skip a service. Either way the decision takes less time than reading this did.

Compare Verdaccio

8 head-to-head comparisons.

Similar developer tools & git apps