Skip to main content

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โ€‹

SolutionCostHostingBest for
npm OrganizationsPaid per userManaged (npmjs.com)Teams already using npm
GitHub PackagesIncluded with GitHub plansManaged (GitHub)GitHub-centric orgs
VerdaccioFree (self-hosted)Self-hostedSmall teams, local dev
ArtifactoryEnterprise pricingManaged or self-hostedEnterprise, multi-format
NexusFree/Pro tiersSelf-hostedEnterprise, 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โ€‹

Store tokens as CI secrets and reference them in .npmrc:

//npm.pkg.github.com/:_authToken=${NPM_TOKEN}
warning

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:

  1. Ensure .npmrc is in the project (with env var placeholders, not actual tokens)
  2. Document required environment variables in your README or contributing guide
  3. Provide setup instructions for obtaining tokens
# Team member setup
export GITHUB_TOKEN=ghp_xxxxxxxxxxxx
npm install