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 yarnWhy 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
.pkginstaller 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/usror/usr/localis always root-owned. It also happens with the official NodeSource repositories. For general Linux permission issues, see also Fix: bash permission denied.
Fix 1: Use nvm (Node Version Manager) — Recommended
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 nodeLinux (Debian/Ubuntu):
sudo apt remove nodejs npmLinux (Fedora/RHEL):
sudo dnf remove nodejs npmIf 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 | bashClose and reopen your terminal, or reload your shell config:
source ~/.bashrc # bash
source ~/.zshrc # zshStep 3: Install Node through nvm
nvm install --ltsStep 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 errorsWhy 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-globalStep 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 ~/.zshrcStep 4: Test it
npm install -g typescript
tsc --versionNote: 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-globalFix 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.tsThis 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/shareOn some Linux systems the path may differ:
sudo chown -R $(whoami) /usr/lib/node_modulesThis 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-packageThis is dangerous for several reasons:
- 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. - File ownership becomes a mess. Some files in your
~/.npmcache end up owned by root, which causes further permission errors for non-sudo installs later. You end up needing sudo for everything. - 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) ~/.npmThen 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 nodeIf 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 --ltsIf 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:
- Use Fix 2 (change npm prefix) — this only requires write access to your home directory, which is almost always available.
- Use npx for one-off tool usage.
- 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'- 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 prefixThere 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 -lLook 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) ~/.npmRelated: If you’re seeing dependency resolution errors instead of permission errors, see Fix: npm ERR! ERESOLVE unable to resolve dependency tree.
Related Articles
Fix: EACCES: permission denied, mkdir / open / unlink (Node.js)
How to fix EACCES permission denied errors in Node.js for mkdir, open, unlink, and scandir operations, covering npm global installs, Docker, NVM, CI/CD, and filesystem permissions.
Fix: bash: ./script.sh: Permission denied (Linux, macOS, WSL)
How to fix 'Permission denied' errors in bash, including chmod +x for scripts, file ownership with chown, EACCES in Node.js, Docker volume permissions, SELinux, ACLs, and WSL issues.
Fix: ENOSPC: System limit for number of file watchers reached
How to fix the ENOSPC file watchers error on Linux by increasing the inotify watch limit, configuring VS Code, optimizing watched files, and handling Docker/WSL edge cases.
Fix: npm ERR! code ELIFECYCLE (errno 1, Failed at script)
How to fix npm ERR! code ELIFECYCLE, npm ERR! errno 1, and npm ERR! Failed at the script errors. Covers reading the real error, node_modules corruption, node-gyp failures, wrong Node version, memory issues, postinstall failures, Windows-specific fixes, and more.