DEV Community

Vimal
Vimal

Posted on

Managing NPM Registries in restricted environments

Problem statement:

Recently we faced a challenge where our organization does not allow us to connect to the public NPM registry to develop locally. However the CI/CD pipelines of the client organization allowed connection to the public registry. As a team, we were pondering on different approaches to handle this unique situation. We could probably use two copies of package.json one for local having reference link to private registry and then another one for CI/CD pipeline. Is this a good approach or is there a better approach? Let us explore this in the article.

Introduction

This is a common scenario in some enterprise environments, developers work behind strict firewalls or disconnected networks that block access to the public NPM registry. Yet, the CI/CD pipelines (like Azure DevOps) may be fully open to the internet. This mismatch creates a development headache—how do you keep your dependencies synced and builds working smoothly?

A common (but bad) idea is to maintain two separate package.json files—one for local dev and one for CI/CD. But this introduces more problems than it solves.

The better, cleaner approach? Use .npmrc files smartly.

In this article let us explore how to:

  • Use a private registry for local development
  • Let Azure DevOps pipelines use the public registry
  • Securely manage credentials
  • Avoid sync issues between environments

❌ Why Two package.json Files Is a Bad Idea

Some teams try to maintain one package.json for local (private registry) and another for CI/CD (public registry).

Here’s why that’s problematic:

  • Dependency drift: Easy to forget syncing both files
  • Lockfile conflicts: package-lock.json mismatches can cause broken builds
  • Hard to audit: Tools like npm audit, dependabot, etc. lose context
  • Increased complexity: More files, more chances of human error

✅ The Right Way: Use .npmrc

Local Development Setup

In your Angular or Node project, create a .npmrc file only for local use:

# .npmrc (DO NOT commit to Git)
registry=https://your-private-registry.company.com/
always-auth=true
Enter fullscreen mode Exit fullscreen mode

Or for scoped packages:

@your-org:registry=https://your-private-registry.company.com/
Enter fullscreen mode Exit fullscreen mode

.gitignore It!

To avoid leaking sensitive registry info, add .npmrc to .gitignore:

.npmrc
Enter fullscreen mode Exit fullscreen mode

Now you can safely run:

npm install
Enter fullscreen mode Exit fullscreen mode

Azure DevOps CI/CD Setup

✅ Option 1: Use Public NPM Registry (Recommended for Pipelines)

Azure DevOps build agents can access the public registry by default. So just omit .npmrc in your repo and let it install dependencies directly from https://registry.npmjs.org.

- script: npm ci
  displayName: 'Install Dependencies'
Enter fullscreen mode Exit fullscreen mode

Option 2: Use Private Registry in CI/CD (If Required)

If your pipeline must use a private registry, create a secret token in Azure DevOps (npm_token) and inject .npmrc at build time:

variables:
  NPM_TOKEN: $(npm_token)

steps:
  - script: |
      echo "registry=https://your-private-registry.company.com/" > .npmrc
      echo "//your-private-registry.company.com/:_authToken=$(NPM_TOKEN)" >> .npmrc
    displayName: 'Configure Private Registry'

  - script: npm ci
    displayName: 'Install Dependencies'
Enter fullscreen mode Exit fullscreen mode

Full Azure DevOps Pipeline for Angular

trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

steps:
- task: NodeTool@0
  inputs:
    versionSpec: '18.x'
  displayName: 'Install Node.js'

- script: |
    echo "registry=https://registry.npmjs.org/" > .npmrc
  displayName: 'Use Public NPM Registry'

- script: npm ci
  displayName: 'Install Dependencies'

- script: npm run build
  displayName: 'Build Angular App'

- task: PublishBuildArtifacts@1
  inputs:
    pathToPublish: 'dist'
    artifactName: 'angular-app'
Enter fullscreen mode Exit fullscreen mode

Local vs CI/CD Setup

Local vs CI/CD NPM Registry Setup

Local vs CI/CD

Key Takeaways

  • Don’t manage two package.json files
  • Use .npmrc to cleanly separate dev and CI registry logic
  • Keep .npmrc out of version control
  • Use secure tokens in Azure DevOps

Bonus Tips

Use npmrc CLI for Switching

If you switch between multiple registries, use npmrc:

npmrc private
npmrc public
Enter fullscreen mode Exit fullscreen mode

Scoped Packages

To avoid overriding global registry settings, scope packages and use this pattern:

@your-org:registry=https://private.registry.com/
Enter fullscreen mode Exit fullscreen mode

Wrapping Up

Using .npmrc properly lets you handle both restricted dev environments and open CI/CD pipelines with ease. It’s clean, secure, and scalable—everything a good DevOps setup should be.

Let me know in the comments your feedback, if you want a complete deploy-to-Azure setup or if you're facing similar restrictions in your organization!

Top comments (0)