npm

Private npm Registry Guide: Setup, Comparison & Best Practices

Complete guide to hosting private npm packages. Compare Verdaccio, GitHub Packages, npm Enterprise, and CloudRepo. Step-by-step setup for npm, Yarn, pnpm, and Bun.

CloudRepo Team
8 min read

Every JavaScript team eventually needs to share internal packages without publishing them to the public npm registry. Whether it’s proprietary business logic, shared UI components, or internal tooling, a private npm registry is the answer.

This guide covers why you need one, how the options compare, and step-by-step setup for npm, Yarn, pnpm, and Bun with CloudRepo.

Why Use a Private npm Registry?

Before evaluating solutions, let’s understand the problems a private registry solves.

Security and IP Protection

Your internal libraries contain proprietary code. Publishing them to the public npm registry exposes your intellectual property. A private registry keeps your packages secure behind authentication.

Terminal window
# These shouldn't be on public npm
npm install @acme/auth-sdk
npm install @acme/billing-core
npm install @acme/design-system

CI/CD Reliability

Depending on Git URLs or local tarballs creates fragile pipelines. A private registry provides stable, versioned packages that survive branch deletions, repository renames, and network hiccups.

Terminal window
# Fragile: depends on Git access and branch existence
npm install git+ssh://git@github.com/acme/shared-utils.git#main
# Reliable: versioned package from your registry
npm install @acme/shared-utils@2.1.0

Access Control

Control who can publish packages and who can install them. Per-user permissions and organization-level isolation keep your packages safe.

Reproducible Builds

Lock files reference registry URLs, not Git commits. A private registry ensures every npm install pulls the exact same artifacts, every time.

Comparing Private npm Registry Options

Verdaccio (Self-Hosted)

Verdaccio is the most popular open-source npm registry.

Pros:

  • Free and open source
  • Full npm compatibility
  • Proxy to public npm included
  • Active community

Cons:

  • You manage the infrastructure (servers, storage, backups, SSL)
  • No built-in high availability
  • Limited enterprise features (RBAC, audit logs)
  • Security patching is your responsibility

Best for: Small teams with DevOps capacity who want full control or need air-gapped environments.

GitHub Packages

GitHub’s built-in package registry supports npm alongside other formats.

Pros:

  • Integrated with GitHub repositories and Actions
  • Free for public packages
  • No additional accounts to manage

Cons:

  • Only supports scoped packages (@scope/package) — unscoped packages are not supported
  • Tied to GitHub ecosystem (vendor lock-in)
  • Storage and transfer limits on free tier
  • Data transfer fees on paid plans

Best for: Teams already on GitHub who only need scoped packages and want minimal configuration.

Important

GitHub Packages limitation: If you need to publish unscoped packages like my-utils (without an @scope/ prefix), GitHub Packages won’t work. CloudRepo supports both scoped and unscoped packages.

npm Enterprise (npmE)

npm’s own enterprise offering for private registries.

Pros:

  • From the npm team — maximum compatibility
  • SSO integration
  • Access management

Cons:

  • Expensive (custom pricing, typically $10,000+/year)
  • npm was acquired by GitHub — the standalone enterprise product’s future is unclear
  • Overlap with GitHub Packages creates confusion

Best for: Large enterprises already invested in the npm ecosystem with budget for premium pricing.

JFrog Artifactory

The enterprise incumbent supporting 20+ package formats.

Pros:

  • Universal format support
  • Mature enterprise features
  • Virtual repositories for aggregation

Cons:

  • Consumption-based pricing creates unpredictable bills
  • Complex setup and administration
  • Expensive support contracts

Pricing reality: Artifactory’s $150/month base quickly escalates. With their consumption model (storage + transfer), moderate CI/CD usage can push costs to $600-1,200/month.

CloudRepo

Managed npm registry with transparent, predictable pricing.

Pros:

  • No egress fees — npm install never incurs transfer charges
  • Scoped AND unscoped packages (unlike GitHub Packages)
  • Works with npm, Yarn, pnpm, and Bun
  • Predictable flat-rate pricing
  • Support included from real engineers

Cons:

  • Fewer advanced features than Artifactory
  • No self-hosted option

Pricing: CloudRepo starts at $199/month with unmetered data transfer and unlimited users.

Quick Comparison

FeatureVerdaccioGitHub PackagesArtifactoryCloudRepo
Setup TimeHoursMinutesHoursMinutes
MaintenanceHighNoneMediumNone
Scoped PackagesYesYesYesYes
Unscoped PackagesYesNoYesYes
Egress FeesN/AYes$0.75-1.25/GBNone
SSO/SAMLDIYGitHub onlyExtra costIncluded
SupportCommunityGitHub supportExtra costIncluded
Starting PriceFree + infraFree (limited)$150/month + usage$199/month

Setting Up CloudRepo as Your Private npm Registry

Step 1: Create Your Repository

Sign up at cloudrepo.io/signup (14-day free trial, no credit card required) and create an npm repository through the web UI.

Your registry URL will be:

https://YOUR_ORG.mycloudrepo.io/repositories/YOUR_REPO

Step 2: Authenticate with npm

Terminal window
# Log in to your private registry
npm login --registry=https://YOUR_ORG.mycloudrepo.io/repositories/YOUR_REPO

Step 3: Configure .npmrc

Create a .npmrc file in your project root (recommended) or configure globally:

.npmrc
registry=https://YOUR_ORG.mycloudrepo.io/repositories/YOUR_REPO
always-auth=true

Step 4: Publish Your First Package

Terminal window
# Publish a scoped package
npm publish --access restricted
# Publish an unscoped package
npm publish

Step 5: Install from Your Registry

Terminal window
# Install works exactly like public npm
npm install @acme/shared-utils
npm install my-internal-tool

Working with Multiple Clients

CloudRepo uses the standard npm registry protocol, so all major JavaScript package managers work out of the box.

Yarn 2+ (Berry)

.yarnrc.yml
npmRegistryServer: "https://YOUR_ORG.mycloudrepo.io/repositories/YOUR_REPO"
npmAlwaysAuth: true
npmAuthToken: "${NPM_TOKEN}"
Terminal window
# Install packages
yarn install
# Publish
yarn npm publish

pnpm

pnpm uses the standard .npmrc file:

.npmrc
registry=https://YOUR_ORG.mycloudrepo.io/repositories/YOUR_REPO
always-auth=true
//YOUR_ORG.mycloudrepo.io/repositories/YOUR_REPO:_authToken=${NPM_TOKEN}
Terminal window
# Install packages
pnpm install
# Publish
pnpm publish

Bun

bunfig.toml
[install]
registry = "https://YOUR_ORG.mycloudrepo.io/repositories/YOUR_REPO"

Or use .npmrc (Bun supports it):

Terminal window
# Install packages
bun install
# Publish (uses npm under the hood)
bunx npm publish

CI/CD Integration

GitHub Actions

.github/workflows/publish.yml
name: Publish Package
on:
release:
types: [published]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://YOUR_ORG.mycloudrepo.io/repositories/YOUR_REPO'
- run: npm ci
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.CLOUDREPO_NPM_TOKEN }}

GitLab CI

.gitlab-ci.yml
publish:
image: node:20
stage: deploy
script:
- echo "//YOUR_ORG.mycloudrepo.io/repositories/YOUR_REPO:_authToken=${CLOUDREPO_NPM_TOKEN}" > .npmrc
- npm publish
only:
- tags

Jenkins

Jenkinsfile
pipeline {
agent { docker { image 'node:20' } }
stages {
stage('Publish') {
steps {
withCredentials([string(credentialsId: 'cloudrepo-npm-token', variable: 'NPM_TOKEN')]) {
sh '''
echo "//YOUR_ORG.mycloudrepo.io/repositories/YOUR_REPO:_authToken=${NPM_TOKEN}" > .npmrc
npm publish
'''
}
}
}
}
}

Best Practices

Scoped vs Unscoped Packages

Use scoped packages for organization-internal code and unscoped for shared libraries:

{
"name": "@acme/auth-sdk",
"version": "2.1.0"
}

Scoped packages make ownership clear and prevent naming collisions with public npm packages.

Use always-auth

Always set always-auth=true in your .npmrc. Without it, npm may attempt unauthenticated requests to your private registry and fail.

Per-Project .npmrc Over Global

Commit a .npmrc to each project rather than relying on global configuration. This ensures:

  • New developers can set up immediately
  • CI/CD environments work without extra configuration
  • Different projects can use different registries
.npmrc
registry=https://YOUR_ORG.mycloudrepo.io/repositories/YOUR_REPO
always-auth=true
# Auth token comes from environment variable — never commit tokens
//YOUR_ORG.mycloudrepo.io/repositories/YOUR_REPO:_authToken=${NPM_TOKEN}

Token Management

  • Generate read-only tokens for CI/CD install steps
  • Generate publish tokens only for release pipelines
  • Rotate tokens on a regular schedule
  • Never commit tokens to source control — use environment variables

Version Management with Dist-Tags

Use dist-tags to manage release channels:

Terminal window
# Publish a stable release (automatically tagged as "latest")
npm publish
# Publish a beta
npm publish --tag beta
# Promote beta to latest
npm dist-tag add @acme/sdk@2.0.0-beta.5 latest

Tip

CloudRepo supports the full npm dist-tag protocol. Use tags like beta, canary, and next to manage release channels without affecting production installs.

Getting Started

Ready to simplify your JavaScript package management?

  1. Start your 14-day free trial — no credit card required
  2. Create an npm repository — takes 30 seconds
  3. Run npm login — authenticate with your registry
  4. npm publish — your first package is live

No egress fees, no per-user pricing, no consumption meters. Just simple, reliable npm package hosting.


Questions about setting up your private npm registry? Our engineering team provides hands-on support for every customer. Contact us for a personalized walkthrough.

Ready to save 90%+ on your repository hosting?

Join the teams who've switched to CloudRepo for better pricing and features.