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. To skip to the product, see CloudRepo’s private npm registry.
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.
# These shouldn't be on public npmnpm install @acme/auth-sdknpm install @acme/billing-corenpm install @acme/design-systemCI/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.
# Fragile: depends on Git access and branch existencenpm install git+ssh://git@github.com/acme/shared-utils.git#main
# Reliable: versioned package from your registrynpm install @acme/shared-utils@2.1.0Access 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), not unscoped ones - 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, enterprise-tier pricing; npm doesn’t publish rates)
- npm was acquired by GitHub, so 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 60+ package formats.
Pros:
- Universal format support
- Mature enterprise features
- Virtual repositories for aggregation
Cons:
- Consumption-based pricing creates unpredictable bills
- Complex setup and administration
- Community support on the Pro plan; 24/7 SLA support starts at Enterprise X
Pricing reality: Artifactory’s Pro base is $150/month at list. With their consumption model (storage + transfer), each GB above the included 25 GB adds $1.25 at the first tier, so 500 GB of monthly consumption costs $743.75 at list.
CloudRepo
A managed private npm registry with transparent, predictable pricing.
Pros:
- 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 $99/month billed annually, with a generous transfer allowance and unlimited users.
Quick Comparison
| Feature | Verdaccio | GitHub Packages | Artifactory | CloudRepo |
|---|---|---|---|---|
| Maintenance | High | None | Medium | None |
| Scoped Packages | Yes | Yes | Yes | Yes |
| Unscoped Packages | Yes | No | Yes | Yes |
| SSO/SAML | DIY | GitHub only | Enterprise X | Included |
| Support | Community | GitHub support | Community on Pro; 24/7 SLA on Enterprise X | Included |
| Starting price | Free + infra | Free (limited) | $150/month list + usage | $99/month billed annually |
Pricing your whole team for a year? Open the ROI calculator
CloudRepo includes single sign-on and human support from the people who build CloudRepo on every plan.
| Feature | JFrog Pro | CloudRepo, every plan |
|---|---|---|
| Single sign-on (SSO) | Not in Pro: JFrog lists SAML and OAuth sign-on for Enterprise X Enterprise X is for organizations who need multi-site capabilities, uptime and support guarantees, and enterprise access controls (ie. OAuth, SAML, CName). JFrog’s pricing page, archived | Included |
| Support | Community support: public documentation, a knowledge base and Stack Overflow Community Support - Available for Free solutions and Pro subscriptions - JFrog maintains public documentation, knowledge base, and engages in conversations on Stack Overflow. JFrog’s pricing page, archived | Human support from the people who build CloudRepo |
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-repoStep 2: Authenticate with npm
# Configure your registry, then run npm loginnpm config set registry=https://your-org.mycloudrepo.io/repositories/your-repo/npm loginnpm login opens your browser, walks you through CloudRepo sign-in, and writes a repo-scoped token to ~/.npmrc automatically. For CI/CD scripts, mint an API token in the admin portal and reference it as _authToken in .npmrc instead. No browser required.
Step 3: Configure .npmrc
After npm login, your ~/.npmrc is configured automatically. For project-local config (e.g., to share with your team):
registry=https://your-org.mycloudrepo.io/repositories/your-repo///your-org.mycloudrepo.io/repositories/your-repo/:_authToken=${NPM_TOKEN}Step 4: Publish Your First Package
# Publish a scoped packagenpm publish --access restricted
# Publish an unscoped packagenpm publishStep 5: Install from Your Registry
# Install works exactly like public npmnpm install @acme/shared-utilsnpm install my-internal-toolWorking with Multiple Clients
CloudRepo uses the standard npm registry protocol, so all major JavaScript package managers work out of the box.
Yarn 2+ (Berry)
npmRegistryServer: 'https://your-org.mycloudrepo.io/repositories/your-repo'npmAlwaysAuth: truenpmAuthToken: '${NPM_TOKEN}'Yarn 4.15 and later wait a day before installing a newly published version. On Yarn 4.17 and later, add this to your scope’s block to install your own packages the day you publish them. On 4.15 or 4.16, update Yarn or install the next day.
npmScopes: yourscope: npmRegistryServer: 'https://your-org.mycloudrepo.io/repositories/your-repo' npmAuthToken: '${NPM_TOKEN}' npmMinimalAgeGate: 0This lets Yarn install versions you published today from your own repository. Delete that line to keep Yarn’s one-day delay.
# Install packagesyarn install
# Publishyarn npm publishpnpm
pnpm uses the standard .npmrc file:
registry=https://your-org.mycloudrepo.io/repositories/your-repo//your-org.mycloudrepo.io/repositories/your-repo:_authToken=${NPM_TOKEN}# Install packagespnpm install
# Publishpnpm publishBun
[install.scopes]"@yourscope" = { token = "$NPM_TOKEN", url = "https://your-org.mycloudrepo.io/repositories/your-repo/" }Or use .npmrc (Bun supports it):
# Install packagesbun install
# Publish (uses npm under the hood)bunx npm publishCI/CD Integration
GitHub Actions
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
publish: image: node:20 stage: deploy script: - echo '//your-org.mycloudrepo.io/repositories/your-repo/:_authToken=${CLOUDREPO_NPM_TOKEN}' > .npmrc - npm publish only: - tagsJenkins
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 ''' } } } }}In both, the single quotes write ${...} into .npmrc as text, and npm replaces it with the variable’s value when it runs, so the file never holds the token.
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.
Give Each Registry a Token Line
npm sends the token on every request to the registry whose //host/path/:_authToken line matches your registry= URL, installs included, so no always-auth setting is needed (npm 11 warns that always-auth is an unknown option).
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
registry=https://your-org.mycloudrepo.io/repositories/your-repo# 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:
# Publish a stable release (automatically tagged as "latest")npm publish
# Publish a betanpm publish --tag beta
# Promote beta to latestnpm dist-tag add @acme/sdk@2.0.0-beta.5 latestTip
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?
- Start your 14-day free trial: no credit card required
- Create an npm repository: takes 30 seconds
- Run
npm login: your browser opens, you authorize once, and your repo-scoped token lands in~/.npmrc npm publish: your first package is live
No per-user pricing. 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.
Competitor figures in this guide were read on 2026-10-06 from JFrog’s pricing (archived), JFrog’s Pro checkout (archived), JFrog’s support page (archived) and JFrog’s SAML documentation (archived). Vendors change prices and plans, so check each page before you decide.
Questions?
We're bootstrapped and founder-run. Book a call or send me an email, and you'll get a real conversation with someone who can help.