Private Packages & Registries
Not every package belongs on the public npm registry. Internal utilities, shared configs, and proprietary code need private distribution channels.
When You Need Private Packagesโ
- Shared utilities across multiple internal projects
- Organization-specific configurations (ESLint configs, TS configs)
- Proprietary business logic that shouldn't be public
- Internal design system components
Options Comparedโ
| Solution | Cost | Hosting | Best for |
|---|---|---|---|
| npm Organizations | Paid per user | Managed (npmjs.com) | Teams already using npm |
| GitHub Packages | Included with GitHub plans | Managed (GitHub) | GitHub-centric orgs |
| Verdaccio | Free (self-hosted) | Self-hosted | Small teams, local dev |
| Artifactory | Enterprise pricing | Managed or self-hosted | Enterprise, multi-format |
| Nexus | Free/Pro tiers | Self-hosted | Enterprise, multi-format |
npm Organizations (Private Packages)โ
npm offers private packages through paid Organizations:
# Publish a private scoped package
npm publish # Scoped packages are private by default
No special registry config needed โ private packages live on the same registry, access-controlled by org membership.
Aliz's npm organization is @aliz.ai. Our packages should be published under that scope (e.g., @aliz.ai/my-package).
GitHub Packages (Private)โ
See Publishing to GitHub Packages โ the same setup works for private packages. Access is controlled by repository visibility and permissions.
Self-Hosted: Verdaccioโ
Verdaccio is a lightweight, open-source npm registry you can run anywhere:
# Install and run
npm install -g verdaccio
verdaccio
# Or with Docker
docker run -it --rm --name verdaccio -p 4873:4873 verdaccio/verdaccio
Configure your project to use it:
# .npmrc
registry=http://localhost:4873
Verdaccio also acts as a caching proxy for the public npm registry, which is useful for CI reliability and offline development.
Verdaccio in productionโ
For team use, deploy Verdaccio behind authentication:
# config.yaml
auth:
htpasswd:
file: ./htpasswd
max_users: 100
packages:
'@my-org/*':
access: $authenticated
publish: $authenticated
proxy: npmjs
'**':
access: $all
proxy: npmjs
Enterprise Registriesโ
Artifactory and Nexus support npm registries alongside other formats (Maven, Docker, PyPI). They offer:
- Fine-grained access control
- Audit logging
- Virtual registries (merge multiple sources)
- Vulnerability scanning
Configuration follows the same .npmrc pattern:
@my-org:registry=https://artifactory.company.com/api/npm/npm-local/
//artifactory.company.com/api/npm/npm-local/:_authToken=${ARTIFACTORY_TOKEN}
Scoped Registry Configurationโ
Use .npmrc to route specific scopes to different registries while keeping everything else on public npm:
# Public npm for everything
registry=https://registry.npmjs.org/
# Private org packages from GitHub Packages
@my-org:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
# Internal tools from Verdaccio
@internal:registry=http://verdaccio.internal:4873
//verdaccio.internal:4873/:_authToken=${VERDACCIO_TOKEN}
CI/CD Authentication Strategiesโ
Environment variables (recommended)โ
Store tokens as CI secrets and reference them in .npmrc:
//npm.pkg.github.com/:_authToken=${NPM_TOKEN}
Never commit tokens directly in .npmrc. Always use environment variable interpolation.
GitHub Actions exampleโ
steps:
- uses: actions/setup-node@v4
with:
registry-url: https://npm.pkg.github.com
scope: '@my-org'
- run: npm ci
env:
NODE_AUTH_TOKEN: ${{ secrets.PACKAGES_TOKEN }}
Multiple registries in CIโ
If you consume from multiple private registries, configure all of them in .npmrc with separate tokens:
- name: Configure registries
run: |
echo "@org-a:registry=https://npm.pkg.github.com" >> .npmrc
echo "//npm.pkg.github.com/:_authToken=${{ secrets.GH_PACKAGES_TOKEN }}" >> .npmrc
echo "@org-b:registry=https://artifactory.company.com/api/npm/npm-local/" >> .npmrc
echo "//artifactory.company.com/api/npm/npm-local/:_authToken=${{ secrets.ARTIFACTORY_TOKEN }}" >> .npmrc
Consuming Private Packagesโ
For team members to install private packages:
- Ensure
.npmrcis in the project (with env var placeholders, not actual tokens) - Document required environment variables in your README or contributing guide
- Provide setup instructions for obtaining tokens
# Team member setup
export GITHUB_TOKEN=ghp_xxxxxxxxxxxx
npm install