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
Or for scoped packages:
@your-org:registry=https://your-private-registry.company.com/
.gitignore
It!
To avoid leaking sensitive registry info, add .npmrc
to .gitignore
:
.npmrc
Now you can safely run:
npm install
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'
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'
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'
Local vs CI/CD Setup
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
Scoped Packages
To avoid overriding global registry settings, scope packages and use this pattern:
@your-org:registry=https://private.registry.com/
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)