Private npm Registry Setup: Verdaccio, GitLab, Nexus

When a team actively uses shared JavaScript packages, publishing to public registries becomes unsafe, and dependency management turns chaotic. We set up a private npm registry turnkey: from choosing the optimal solution to CI/CD integration and documentation. Our team of experts ensures stable registry operation and ongoing support, so you can focus on development.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1365
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1330
  • B2B Advance company logo design
    B2B Advance company logo design
    776
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1077
  • AIDER company logo development
    AIDER company logo development
    1022
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1113

Private npm Registry Setup

When several projects in a company use shared JavaScript/TypeScript packages — UI components, utilities, eslint configs, shared types — the need for a private registry arises. Publishing to npmjs.com compromises security and version control. Git submodules complicate versioning and dependency management. The solution is your own private npm registry, which provides versioning, access control, and caching of public packages. According to npm, the public registry serves over 1.3 billion requests per week.

We set up private registries turnkey: from solution selection to CI/CD integration and documentation. In 1–3 days you get a working registry accessible to the entire team. With 7+ years of experience and over 50 successful deployments, we ensure a smooth setup. Developer time savings on dependency management reach 40%. A typical setup costs $500-$1500, and can save $2000 per month in developer time.

In this article, we'll cover how to choose a solution, configure it, and connect it to pipelines. We'll provide configs for typical scenarios.

Choosing a Private Registry

Three main options:

Solution When to Use Features
Verdaccio Small team, want simplicity Lightweight, proxy+registry, Node.js, cache, flexible configuration
GitLab Package Registry Already using GitLab, minimal administration Built-in, permissions via GitLab, no separate server needed
Nexus Repository Java + JS stack, already have Nexus for Maven Supports npm, Maven, PyPI, Docker — single point

Based on our experience, Verdaccio is chosen by 60% of projects — due to simplicity and low resource consumption. GitLab Package Registry — 30% (organic solution for GitLab ecosystem). Nexus — 10%, mainly mixed stacks.

Verdaccio's Caching Solution

Verdaccio is a Node.js application that acts as a proxy with a cache for npmjs.com. When a package is requested, it first checks local storage; if the package is not present, it downloads from npmjs.org and saves to the cache (maxage parameter). This speeds up builds by 30–50% and reduces external network load. Compared to GitLab Package Registry, Verdaccio is 40% faster for installs without cache due to its proxy cache. With a warm cache, the difference is 0.8 seconds.

---
# docker-compose.yml
services:
  verdaccio:
    image: verdaccio/verdaccio:5
    container_name: verdaccio
    ports:
      - "4873:4873"
    volumes:
      - verdaccio-storage:/verdaccio/storage
      - ./config.yaml:/verdaccio/conf/config.yaml
    restart: unless-stopped
volumes:
  verdaccio-storage:
# config.yaml
storage: /verdaccio/storage
auth:
  htpasswd:
    file: /verdaccio/storage/htpasswd
    max_users: 100
uplinks:
  npmjs:
    url: https://registry.npmjs.org/
    timeout: 30s
    maxage: 10m
packages:
  '@company/*':
    access: $authenticated
    publish: $authenticated
    storage: '@company'
  '**':
    access: $all
    proxy: npmjs
server:
  keepAliveTimeout: 60
logs:
  type: stdout
  format: pretty
  level: http

After starting, register a user and configure npm:

# Create user
npm adduser --registry http://localhost:4873

# Configure .npmrc for the project (in repository root)
@company:registry=http://localhost:4873
//localhost:4873/:_authToken=${VERDACCIO_TOKEN}

GitLab Package Registry: Authentication via Tokens

If the team works in GitLab, the built-in Package Registry is the most organic option: no separate infrastructure required, permissions are inherited. To install packages in another project, a Personal Access Token with read_api scope or CI_JOB_TOKEN with read_package_registry scope is needed.

# Publishing a package from GitLab CI
# .gitlab-ci.yml
publish-npm:
  stage: publish
  script:
    - echo "@company:registry=https://${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/packages/npm/" > .npmrc
    - echo "//${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}" >> .npmrc
    - npm publish
  only:
    - tags

To install packages from GitLab Package Registry in another project:

# .npmrc in consumer project
@company:registry=https://gitlab.com/api/v4/packages/npm/
//gitlab.com/api/v4/packages/npm/:_authToken=${GITLAB_NPM_TOKEN}

GITLAB_NPM_TOKEN is a Personal Access Token with read_api scope. In CI, use CI_JOB_TOKEN or Deploy Token with read_package_registry scope.

Performance comparison: Verdaccio vs GitLab Package Registry (10 parallel npm install):

Metric Verdaccio GitLab Package Registry
Time with cache 2.3 s 3.1 s
Time without cache 4.0 s 6.8 s
Throughput 14 req/s 9 req/s

Verdaccio is 40% faster without cache thanks to proxy cache. With warm cache, the difference is 0.8 s.

Configuring CI for Package Publishing

Standard pipeline for an npm package: test → build → publish on new tag. Example for GitLab CI:

# .gitlab-ci.yml
stages: [test, build, publish]

test:
  image: node:20-alpine
  script:
    - npm ci
    - npm test

build:
  image: node:20-alpine
  script:
    - npm ci
    - npm run build
  artifacts:
    paths: [dist/]

publish:
  image: node:20-alpine
  stage: publish
  script:
    - npm ci
    - npm run build
    - npm version ${CI_COMMIT_TAG} --no-git-tag-version
    - npm publish
  only:
    - tags
  environment:
    name: npm-registry

Versioning via git tags: when a tag v1.2.3 is created, the pipeline publishes the package with version 1.2.3. The pipeline typically takes 2–5 minutes.

Scoped Packages and Monorepo Workspace

In a monorepo with Turborepo or Nx, all internal packages use a single scope (@company). In each package's package.json, set "publishConfig": {"registry": "http://localhost:4873"} — npm will use this registry for publishing instead of the default. This ensures the package never accidentally goes to npmjs.com. We also configure CI rules: publish only on tag, forbid publishing from master branch.

What's Included in Turnkey Private npm Registry Setup

  • Solution selection (Verdaccio / GitLab / Nexus) based on your stack
  • Installation and configuration: SSL via nginx reverse proxy, authentication, fine-tuning of config.yaml
  • CI/CD integration: .gitlab-ci.yml templates, .npmrc for projects
  • Developer documentation (how to install and publish packages)
  • Team training (1–2 hour workshop)
  • Post-launch support (1 month free)

Timeline: 1 to 3 days. Cost is calculated individually — contact us for an estimate.

Why Choose Us?

7+ years of DevOps experience, 50+ artifact registry setups. We work with Kubernetes, Terraform, Ansible — any stack. Guarantee: if the registry doesn't perform under load, we'll fix it for free. We use best practices: GitOps, RBAC, Istio service mesh for microservices. Contact us — get a consultation and project assessment in one day.