Fix: EACCES permission denied when installing npm packages globally

The Error

You try to install an npm package globally and get:

npm ERR! Error: EACCES: permission denied, access '/usr/local/lib/node_modules'

Variations of this error include:

npm ERR! Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/some-package'
npm ERR! Error: EACCES: permission denied, access '/usr/local/bin'
npm ERR! Error: EACCES: permission denied, rename '/usr/local/lib/node_modules/some-package'

The command that triggers it is usually something like:

npm install -g typescript
npm install -g create-react-app
npm install -g yarn

Why This Happens

When you install Node.js from the official installer, a package manager like Homebrew, or your distro’s default repositories, the global node_modules directory ends up in a system-owned location — typically /usr/local/lib/node_modules on macOS or /usr/lib/node_modules on some Linux distributions.

These directories are owned by root. Your regular user account does not have write access to them. When npm tries to write files there during a global install, the operating system blocks it.

This is not an npm bug. It is standard Unix permission enforcement. The real problem is that Node was installed in a way that requires root privileges for global packages.

macOS vs Linux

  • macOS: This commonly happens after installing Node from the official .pkg installer or via Homebrew. The global prefix is usually /usr/local, which on modern macOS may be owned by root even if Homebrew previously made it user-writable.
  • Linux: This happens with distro-packaged Node (apt install nodejs, dnf install nodejs) because the prefix /usr or /usr/local is always root-owned. It also happens with the official NodeSource repositories. For general Linux permission issues, see also Fix: bash permission denied.

This is the best long-term solution. nvm installs Node in your home directory (~/.nvm/), so every global install is user-owned. No permission issues, ever.

Step 1: Uninstall your current system Node (optional but clean)

macOS (Homebrew):

brew uninstall node

Linux (Debian/Ubuntu):

sudo apt remove nodejs npm

Linux (Fedora/RHEL):

sudo dnf remove nodejs npm

If you installed from the official .pkg on macOS, you can leave it — nvm will override it via your shell PATH.

Step 2: Install nvm

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash

Close and reopen your terminal, or reload your shell config:

source ~/.bashrc    # bash
source ~/.zshrc     # zsh

Step 3: Install Node through nvm

nvm install --lts

Step 4: Verify

which node
# Should show something like: /home/youruser/.nvm/versions/node/v22.x.x/bin/node

npm install -g typescript
# Works without sudo or permission errors

Why this works: nvm places Node and its global node_modules inside ~/.nvm/, which your user fully owns. There is never a reason to use sudo with nvm-managed Node.

Fix 2: Change npm’s Default Directory

If you don’t want to use nvm, you can tell npm to store global packages in a directory you own.

Step 1: Create a directory for global packages

mkdir -p ~/.npm-global

Step 2: Configure npm to use it

npm config set prefix '~/.npm-global'

Step 3: Add the new directory to your PATH

Add this line to your shell config file (~/.bashrc, ~/.zshrc, or ~/.profile):

export PATH="$HOME/.npm-global/bin:$PATH"

Then reload:

source ~/.bashrc    # or ~/.zshrc

Step 4: Test it

npm install -g typescript
tsc --version

Note: This only fixes npm global installs. If you also use npx or corepack, make sure they respect the same prefix. If you’re hitting dependency resolution errors instead, see Fix: npm ERR! ERESOLVE unable to resolve dependency tree. You can verify your current prefix with:

npm config get prefix
# Should show: /home/youruser/.npm-global

Fix 3: Use npx Instead of Global Install

Many tools that people install globally don’t actually need to be installed globally. npx (bundled with npm 5.2+) runs a package without installing it:

# Instead of:
npm install -g create-react-app
create-react-app my-app

# Do this:
npx create-react-app my-app
# Instead of:
npm install -g typescript
tsc file.ts

# Do this:
npx tsc file.ts

This avoids the permission problem entirely because nothing is written to the global directory. The package is temporarily cached in a user-owned location.

When npx is NOT a replacement: If you need a CLI tool available everywhere at all times (like nodemon, pm2, or http-server), a proper global install via Fix 1 or Fix 2 is better.

Fix 4: Fix Permissions with chown

You can change ownership of the global directory to your user:

sudo chown -R $(whoami) /usr/local/lib/node_modules
sudo chown -R $(whoami) /usr/local/bin
sudo chown -R $(whoami) /usr/local/share

On some Linux systems the path may differ:

sudo chown -R $(whoami) /usr/lib/node_modules

This works but has downsides:

  • It changes ownership of shared system directories, which can cause problems if other software (like Homebrew on macOS) also writes there.
  • OS updates or other package managers may reset these permissions.
  • It is a workaround, not a root-cause fix. Fix 1 or Fix 2 are cleaner.

Why You Should Never Use sudo npm install -g

You may see advice online suggesting:

# DO NOT DO THIS
sudo npm install -g some-package

This is dangerous for several reasons:

  1. npm runs arbitrary scripts. Packages can define preinstall, postinstall, and other lifecycle scripts. Running them as root means a malicious or compromised package can do anything to your system — delete files, install backdoors, modify system configs.
  2. File ownership becomes a mess. Some files in your ~/.npm cache end up owned by root, which causes further permission errors for non-sudo installs later. You end up needing sudo for everything.
  3. It masks the real problem. The correct fix is to not require root privileges for Node development tooling at all.

If you have already run sudo npm install -g and now have mixed ownership in your cache, clean it up:

sudo chown -R $(whoami) ~/.npm

Then apply Fix 1 or Fix 2 to prevent the problem going forward.

Still Not Working?

nvm is installed but you still get EACCES

This usually means your shell is still using the system Node instead of the nvm-managed one.

Check which Node is active:

which node

If it shows /usr/local/bin/node or /usr/bin/node instead of a path inside ~/.nvm/, nvm is not loaded in your current session. Verify nvm is sourced in your shell config:

# This block should be in your ~/.bashrc or ~/.zshrc:
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

Then open a new terminal and run:

nvm use --lts

If you are using a shell other than bash or zsh (like fish), note that nvm does not support fish natively. Use nvm.fish or bass instead.

Corporate machine with restricted permissions

On managed workstations where you cannot install nvm or change system directories:

  1. Use Fix 2 (change npm prefix) — this only requires write access to your home directory, which is almost always available.
  2. Use npx for one-off tool usage.
  3. If your home directory is also restricted (network-mounted home with quotas), ask your IT team to set up a local writable directory and point npm’s prefix there:
npm config set prefix '/opt/local/youruser/npm-global'
  1. On some corporate setups, Node is installed via tools like Volta or asdf instead of nvm. These work the same way — they install Node per-user. Check if your company already has one configured.

Error persists after changing prefix

If you changed the prefix with npm config set prefix but still get EACCES, check for a conflicting global .npmrc:

npm config list
npm config get prefix

There may be a system-level config file at /etc/npmrc or a project-level .npmrc overriding your user setting. You can check all config sources:

npm config list -l

Look for the prefix value and confirm it points to your user-owned directory.

EACCES on ~/.npm (cache directory)

A different but related error:

npm ERR! Error: EACCES: permission denied, mkdir '/home/user/.npm/_cacache'

This typically happens because you previously ran npm with sudo, and now root owns parts of the cache. Fix it with:

sudo chown -R $(whoami) ~/.npm

Related: If you’re seeing dependency resolution errors instead of permission errors, see Fix: npm ERR! ERESOLVE unable to resolve dependency tree.

Related Articles