npm

Node Package Manager - JavaScript Package Ecosystem

About npm

npm (Node Package Manager) is the default package manager for Node.js and the world's largest software registry. With over 2 million packages, npm is essential not just for JavaScript developers, but for system administrators who need to install and manage command-line tools, utilities, and automation scripts.

Why Sysadmins Need npm:

  • CLI Tools: Many modern utilities distributed via npm (serverless, aws-cdk, pm2)
  • Build Tools: Webpack, Babel, and other essential tooling
  • Automation: Task runners, deployment scripts, monitoring tools
  • Universal Format: Cross-platform package distribution
  • Version Control: Reproducible environments with package.json

Key Concepts:

  • package.json: Project manifest - lists dependencies and scripts
  • node_modules: Directory where packages are installed
  • package-lock.json: Locks exact versions for reproducibility
  • Global vs Local: System-wide tools vs project-specific packages
  • Semantic Versioning: Major.Minor.Patch (1.2.3)
  • Scripts: Custom commands defined in package.json
  • Dependencies: Production code requirements
  • DevDependencies: Development/build tools only

Common Use Cases:

  • Installing command-line tools globally (pm2, serverless, newman)
  • Managing project dependencies for Node.js applications
  • Running build scripts and automation tasks
  • Auditing and fixing security vulnerabilities
  • Publishing internal packages to private registries
  • Creating reproducible development environments
Installation
# npm comes with Node.js - install Node.js first # Ubuntu/Debian (via NodeSource repository) $ curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - $ sudo apt-get install -y nodejs # Or via package manager (may be older version) $ sudo apt install nodejs npm # RHEL/CentOS $ curl -fsSL https://rpm.nodesource.com/setup_lts.x | sudo bash - $ sudo yum install nodejs # macOS (via Homebrew) $ brew install node # Verify installation $ node --version v20.10.0 $ npm --version 10.2.3
Essential npm Commands
Command Description
npm install [package] Install package locally
npm install -g [package] Install package globally (system-wide)
npm uninstall [package] Remove package
npm update [package] Update package to latest compatible version
npm list List installed packages
npm search [term] Search npm registry
npm init Create new package.json
npm run [script] Execute script from package.json
npm audit Check for security vulnerabilities
npm publish Publish package to registry
Common Options
Option Description
-g, --global Install globally (system-wide)
--save, -S Add to dependencies (default)
--save-dev, -D Add to devDependencies
--save-exact, -E Pin exact version (no ^ or ~)
--production Skip devDependencies
--dry-run Simulate without making changes
--json Output in JSON format
--depth=N Limit dependency tree depth
Detailed Examples

Example 1Installing Packages Locally and Globally

Understanding local vs global installation:

$ # Create project directory $ mkdir my-project && cd my-project # Initialize npm project $ npm init -y
Wrote to /home/craig/my-project/package.json: { "name": "my-project", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
# Install package locally (project-specific) $ npm install lodash # Install package globally (system-wide CLI tool) $ sudo npm install -g pm2 # Verify installations $ ls node_modules/ lodash $ which pm2
/usr/local/bin/pm2

Explanation:

  • npm init -y: Create package.json with defaults
  • Local install: Adds to ./node_modules/
  • Local installs added to package.json automatically
  • -g: Install globally to system PATH
  • Global installs accessible as commands anywhere
  • Use local for libraries, global for CLI tools
Tip: Avoid sudo npm install -g by configuring npm to use user directory: npm config set prefix ~/.npm-global then add ~/.npm-global/bin to PATH. Safer and avoids permission issues.

Example 2Managing Dependencies in package.json

Understanding dependencies and version specifications:

$ # Install production dependency $ npm install express $ # Install development dependency $ npm install --save-dev jest $ # Install exact version $ npm install --save-exact react@18.2.0 $ cat package.json
{ "name": "my-project", "version": "1.0.0", "dependencies": { "express": "^4.18.2", "react": "18.2.0" }, "devDependencies": { "jest": "^29.7.0" } }
# Install all dependencies from package.json $ npm install # Install only production dependencies $ npm install --production

Explanation:

  • dependencies: Required for production
  • devDependencies: Only for development/testing
  • ^4.18.2: Caret - allows minor/patch updates (4.x.x)
  • ~4.18.2: Tilde - allows only patch updates (4.18.x)
  • 18.2.0: Exact version - no updates
  • --save-exact: Pin versions for reproducibility
Best Practice: Use --save-exact for production applications to ensure reproducible builds. For libraries, use caret (^) to allow compatible updates. Check package-lock.json into version control to lock all transitive dependencies.

Example 3Listing and Searching Packages

Find installed packages and search for new ones:

$ # List all installed packages $ npm list --depth=0
my-project@1.0.0 ├── express@4.18.2 ├── lodash@4.17.21 └── react@18.2.0
# List globally installed packages $ npm list -g --depth=0
/usr/local/lib ├── npm@10.2.3 └── pm2@5.3.0
# Show package details $ npm view express
express@4.18.2 | MIT | deps: 30 | versions: 279 Fast, unopinionated, minimalist web framework https://expressjs.com/ dist .tarball: https://registry.npmjs.org/express/-/express-4.18.2.tgz .shasum: 3fabe08af745055f92d7f8896a33c3b8e7d3a2b1 dependencies: accepts: ~1.3.8 body-parser: 1.20.1 ... published a year ago by dougwilson <doug@somethingdoug.com>
# Search npm registry $ npm search "process manager"
NAME | DESCRIPTION | AUTHOR | DATE pm2 | Production process | =alexa… | 2023-11-15 forever | A simple CLI tool | =index… | 2020-03-12 ...

Explanation:

  • npm list: Show dependency tree
  • --depth=0: Only show top-level packages
  • npm view: Show package information from registry
  • npm search: Find packages by keyword
  • Use npm list -g to audit global tools
  • Check npmjs.com for detailed package docs
Tip: Find outdated packages: npm outdated. Shows current, wanted, and latest versions. Use npm update to update within semver ranges, or npm install package@latest for major updates.

Example 4Using npm Scripts for Automation

Define custom scripts in package.json:

$ cat > package.json << 'EOF' { "name": "my-app", "version": "1.0.0", "scripts": { "start": "node server.js", "dev": "nodemon server.js", "test": "jest --coverage", "lint": "eslint .", "build": "webpack --mode production", "deploy": "npm run build && scp -r dist/ server:/var/www/", "backup": "tar -czf backup-$(date +%Y%m%d).tar.gz src/", "clean": "rm -rf node_modules dist", "prerestart": "echo 'Stopping server...'", "restart": "pm2 restart server.js", "postrestart": "echo 'Server restarted!'" } } EOF # Run scripts $ npm run start # Runs: node server.js $ npm run test # Runs: jest --coverage $ npm run deploy # Runs build, then deploy # Special scripts (don't need 'run') $ npm start # Shorthand for npm run start $ npm test # Shorthand for npm run test
> my-app@1.0.0 start > node server.js Server listening on port 3000...

Explanation:

  • npm run [script]: Execute custom script
  • Scripts can call other npm scripts
  • &&: Chain commands (runs if previous succeeds)
  • pre* and post*: Auto-run hooks
  • start, test: Special - don't need "run"
  • Scripts see node_modules/.bin in PATH
Real-World Usage: npm scripts are your build system. Replace Makefiles with package.json scripts. Common patterns: dev (development server), build (production build), test (run tests), lint (code quality), deploy (deployment). Keep CI/CD simple: npm test && npm run build.

Example 5Security Auditing and Vulnerability Fixes

Check for and fix security vulnerabilities:

$ # Check for vulnerabilities $ npm audit
# npm audit report lodash <=4.17.20 Severity: high Prototype Pollution - https://github.com/advisories/GHSA-... No fix available node_modules/lodash 3 vulnerabilities (1 low, 1 moderate, 1 high) To address issues that do not require attention, run: npm audit fix To address all issues (including breaking changes), run: npm audit fix --force
# Get detailed JSON report $ npm audit --json > audit-report.json # Fix vulnerabilities automatically $ npm audit fix
added 1 package, removed 2 packages, and changed 3 packages in 2s 3 vulnerabilities (1 low, 1 moderate, 1 high) fixed 2 of 3 vulnerabilities in 45 packages 1 vulnerability required manual review and could not be updated
# Force fix (may include breaking changes) $ npm audit fix --force # Check audit level in CI/CD $ npm audit --audit-level=high # Exit code 1 if high+ vulnerabilities found

Explanation:

  • npm audit: Scan dependencies for known vulnerabilities
  • npm audit fix: Auto-update to secure versions
  • --force: Install breaking changes if needed
  • --audit-level: Set severity threshold
  • Reports link to CVE details and advisories
  • Essential for compliance and security
Security Practice: Run npm audit regularly, especially before deployments. Add to CI/CD: npm audit --audit-level=moderate to fail builds with vulnerabilities. Review audit fix --force changes carefully in dev before production. Subscribe to security advisories for critical packages.

Example 6Working with package-lock.json

Understanding and managing the lock file:

$ # Install packages (creates/updates lock file) $ npm install express lodash $ ls -lh package*.json
-rw-r--r-- 1 craig users 285 Dec 16 package.json -rw-r--r-- 1 craig users 97K Dec 16 package-lock.json
$ # View locked versions $ cat package-lock.json | head -20
{ "name": "my-project", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "my-project", "version": "1.0.0", "dependencies": { "express": "^4.18.2", "lodash": "^4.17.21" } }, "node_modules/express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", "integrity": "sha512-5/PsL6iGPdfQ/..." }, ...
# Clean install from lock file (CI/CD) $ rm -rf node_modules $ npm ci
added 57 packages in 3s
# Update lock file without installing $ npm install --package-lock-only

Explanation:

  • package-lock.json: Locks exact versions of ALL dependencies
  • Includes transitive dependencies (dependencies of dependencies)
  • npm ci: Clean install from lock file (faster, reproducible)
  • npm install: Updates lock file if needed
  • Lock file ensures identical installs across team/environments
  • ALWAYS commit package-lock.json to git
CI/CD Best Practice: Use npm ci in automated builds, not npm install. It's faster, stricter, and won't modify package-lock.json. Fails if package.json and lock file are out of sync. Perfect for production deployments and testing.

Example 7Installing Global CLI Tools

Manage system-wide command-line utilities:

$ # Install commonly used global tools $ npm install -g \ pm2 \ nodemon \ http-server \ npm-check-updates \ tldr # Verify installations $ pm2 --version
5.3.0
$ http-server --version
v14.1.1
# Use installed tools $ http-server /var/www/html -p 8080
Starting up http-server, serving /var/www/html http-server version: v14.1.1 Available on: http://127.0.0.1:8080 http://192.168.1.100:8080
# List all global packages $ npm list -g --depth=0 # Update global package $ npm update -g pm2 # Uninstall global package $ npm uninstall -g http-server

Explanation:

  • pm2: Production process manager
  • nodemon: Auto-restart on file changes (development)
  • http-server: Simple static file server
  • npm-check-updates: Find outdated dependencies
  • tldr: Simplified man pages
  • Global tools accessible from any directory
Tip: Essential global tools for sysadmins: pm2 (process management), npm-check-updates (dependency updates), serve (quick file server), localtunnel (expose local server), npx (run packages without installing).

Example 8Using npx to Run Packages Without Installing

Execute packages without global installation:

$ # Run package without installing $ npx cowsay "Hello from npx!"
_________________ < Hello from npx! > ----------------- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || ||
# Run specific version $ npx create-react-app@latest my-app # Run from GitHub $ npx github:user/repo # Execute with arguments $ npx http-server -p 3000 -o # Check outdated packages $ npx npm-check-updates
Checking /home/craig/project/package.json [====================] 15/15 100% express ^4.17.1 → ^4.18.2 lodash ^4.17.20 → ^4.17.21 Run ncu -u to upgrade package.json

Explanation:

  • npx: Execute packages without installing globally
  • Downloads package temporarily to cache
  • Perfect for one-off commands or testing
  • Always runs latest version (unless specified)
  • Cleaner than installing global packages
  • Comes bundled with npm 5.2+
Modern Practice: Prefer npx over global installs for tools you don't use daily. Examples: npx create-react-app, npx eslint, npx prettier. Ensures latest version and avoids global namespace pollution. Great for CI/CD and scripts.

Example 9Configuring npm and Registry Settings

Customize npm behavior and use private registries:

$ # View all npm config $ npm config list
; "user" config from /home/craig/.npmrc init-author-name = "Craig" init-license = "MIT" prefix = "/home/craig/.npm-global" ; "global" config from /usr/local/etc/npmrc registry = "https://registry.npmjs.org/"
# Set config values $ npm config set init-author-name "Craig Walker" $ npm config set init-author-email "craig@example.com" $ npm config set init-license "MIT" # Set private registry $ npm config set registry https://npm.company.com/ # Scope-specific registry $ npm config set @company:registry https://npm.company.com/ # View specific config $ npm config get registry
https://npm.company.com/
# Set authentication token $ npm config set //npm.company.com/:_authToken "abc123..." # Edit config file directly $ npm config edit # Reset to defaults $ npm config delete registry

Explanation:

  • Config stored in ~/.npmrc (user) and /usr/local/etc/npmrc (global)
  • init-*: Defaults for npm init
  • prefix: Where global packages install
  • registry: Package registry URL
  • Scoped packages can use different registries
  • Authentication via tokens, not passwords
Enterprise Setup: For private packages, use scoped registry: public packages from npmjs.org, company packages from private registry. Configure once: npm config set @company:registry https://npm.company.com/. Then npm install @company/package uses private registry automatically.

Example 10Complete Project Setup and Deployment

Real-world example: Initialize, develop, and deploy Node.js app:

$ # Initialize new project $ mkdir myapp && cd myapp $ npm init -y # Install production dependencies $ npm install express dotenv # Install dev dependencies $ npm install --save-dev nodemon eslint jest # Create app structure $ cat > server.js << 'EOF' const express = require('express'); const app = express(); const PORT = process.env.PORT || 3000; app.get('/', (req, res) => { res.json({ message: 'Hello World!' }); }); app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); }); EOF # Update package.json scripts $ npm pkg set scripts.start="node server.js" $ npm pkg set scripts.dev="nodemon server.js" $ npm pkg set scripts.test="jest" $ npm pkg set scripts.lint="eslint ." $ cat package.json
{ "name": "myapp", "version": "1.0.0", "scripts": { "start": "node server.js", "dev": "nodemon server.js", "test": "jest", "lint": "eslint ." }, "dependencies": { "dotenv": "^16.3.1", "express": "^4.18.2" }, "devDependencies": { "eslint": "^8.54.0", "jest": "^29.7.0", "nodemon": "^3.0.2" } }
# Development workflow $ npm run dev # Start dev server with auto-reload $ npm run lint # Check code quality $ npm test # Run tests # Production deployment $ npm ci --production # Install production deps only $ npm start # Start production server # Or with PM2 $ npm install -g pm2 $ pm2 start server.js --name myapp $ pm2 save $ pm2 startup
[PM2] Spawning PM2 daemon with pm2_home=/home/craig/.pm2 [PM2] PM2 Successfully daemonized [PM2] Starting server.js in fork_mode (1 instance) [PM2] Done. ┌────┬──────────┬─────────┬─────────┬─────────┬──────────┐ │ id │ name │ mode │ ↺ │ status │ cpu │ ├────┼──────────┼─────────┼─────────┼─────────┼──────────┤ │ 0 │ myapp │ fork │ 0 │ online │ 0% │ └────┴──────────┴─────────┴─────────┴─────────┴──────────┘

Explanation:

  • Complete project lifecycle from init to deployment
  • npm pkg set: Modern way to update package.json
  • Separate dev and production dependencies
  • Scripts for all development tasks
  • npm ci --production: Production deployment
  • PM2 for production process management
Production Checklist: (1) Use npm ci not npm install, (2) Set NODE_ENV=production, (3) Install only production deps (--production), (4) Run security audit, (5) Use process manager (PM2, systemd), (6) Enable monitoring and logging, (7) Configure reverse proxy (nginx).
Related Commands and Tools
Common Pitfalls and Solutions
Pitfall 1: Running npm as Root/Sudo

Using sudo npm install -g causes permission problems.

Solution: Configure npm to install globally to user directory: npm config set prefix ~/.npm-global, add export PATH=~/.npm-global/bin:$PATH to ~/.bashrc. Or use nvm which handles this automatically.

Pitfall 2: Not Committing package-lock.json

Omitting lock file from git causes inconsistent installs across team.

Solution: ALWAYS commit package-lock.json to version control. It ensures everyone gets identical dependencies. Only exception: publishing libraries (not applications) where you want consumers to get latest compatible versions.

Pitfall 3: Using npm install in CI/CD

npm install can update lock file, causing build inconsistencies.

Solution: Use npm ci in automated builds. It's faster, stricter, and won't modify package-lock.json. Fails if files are out of sync, catching issues early.

Pitfall 4: Ignoring Security Audits

Deploying applications with known vulnerabilities.

Solution: Run npm audit before deployments. Add to CI/CD: npm audit --audit-level=moderate. Set up automated scanning (Snyk, Dependabot). Subscribe to security advisories for critical packages.

Pitfall 5: Installing Everything as Dependencies

Build tools and test frameworks in production dependencies.

Solution: Use --save-dev for development tools: npm install --save-dev jest eslint webpack. Production deploys skip devDependencies with --production flag, reducing size and attack surface.

Pitfall 6: Massive node_modules Directory

node_modules can grow to hundreds of MB or GB.

Solution: Don't commit node_modules to git (add to .gitignore). Use npm prune to remove extraneous packages. Consider pnpm for space efficiency. For Docker: use multi-stage builds and .dockerignore.

Pro Tips and Best Practices
Tip 1: Use .npmrc for Team Consistency

Project-level config ensures team uses same settings:

$ cat > .npmrc << 'EOF' save-exact=true package-lock=true engine-strict=true audit-level=moderate EOF

Commit .npmrc to git. Team gets consistent behavior automatically.

Tip 2: Speed Up npm install

Several techniques to faster installations:

# Use npm ci (clean install) in CI/CD $ npm ci # Skip optional dependencies $ npm install --no-optional # Reduce log verbosity $ npm install --loglevel=error # Parallel downloads (default in npm 7+) # Or try pnpm for even faster installs $ npx pnpm install

Consider npm caching in CI/CD environments.

Tip 3: Semantic Versioning Ranges

Understand version constraints:

# Exact version "package": "1.2.3" # Patch updates only (1.2.x) "package": "~1.2.3" # Minor updates only (1.x.x) "package": "^1.2.3" # Any version >= 1.2.3 "package": " >=1.2.3" # Latest version "package": "latest"

Use ^ for libraries, exact for critical dependencies, ~= for security patches only.

Tip 4: Clean Up Unused Dependencies

Find and remove packages you're not using:

$ npx depcheck
Unused dependencies * lodash * moment Missing dependencies * axios (used in src/api.js) $ npm uninstall lodash moment $ npm install axios

Keeps package.json clean and reduces security surface area.

Tip 5: Environment-Specific Configuration

Use .env files and separate configs:

$ npm install dotenv $ cat > .env << 'EOF' NODE_ENV=production PORT=3000 DB_HOST=localhost API_KEY=secret123 EOF # In code require('dotenv').config(); const port = process.env.PORT || 3000;

Never commit .env to git. Use .env.example as template.

Tip 6: Automate Dependency Updates

Keep dependencies current without manual work:

# Check for updates $ npx npm-check-updates
express ^4.17.1 → ^4.18.2 lodash ^4.17.20 → ^4.17.21 $ Run ncu -u to upgrade package.json
# Update package.json $ npx ncu -u # Install updated versions $ npm install

Or use Dependabot/Renovate for automated PRs.

Historical Note: npm was created by Isaac Z. Schlueter in 2010 as a package manager for Node.js. It quickly became the default package manager and grew into the world's largest software registry with over 2 million packages. npm Inc. was acquired by GitHub in 2020 (which Microsoft owns), ensuring continued investment in the ecosystem. The name officially stands for "npm is not an acronym" (recursive acronym joke), though many backronyms exist.
Quick Reference Cheat Sheet
Task Command
Initialize project npm init -y
Install package locally npm install package
Install package globally npm install -g package
Install dev dependency npm install --save-dev package
Install from package.json npm install
Clean install (CI/CD) npm ci
Update packages npm update
Uninstall package npm uninstall package
List installed packages npm list --depth=0
Check for outdated npm outdated
Security audit npm audit
Fix vulnerabilities npm audit fix
Run script npm run script-name
Run without installing npx package
View package info npm view package