Don’t use `sudo` with `npm`

Andrew Crites
8 min readApr 9, 2015

--

Running sudo npm install -g seems to be pretty common advice on the Internet. mean.io, a prominent web framework compilation generator/library even suggests doing it on their own homepage.

Using sudo npm install (and potentially sudo npm <anything>) is a bad idea ™. This is an issue for at least a few reasons:

  • npm install has the ability to run arbitrary scripts. Due to how npm is set up and the fact that you can alter the registry and it can use DNS, it is possible that you will accidentally install a malicious package in general, install a malicious package masquerading as a perfectly valid package, or install a package with good intentions that may run scripts that are somehow detrimental to your system if run as root.
  • Running sudo npm install (without -g) will create a local directory that can only be altered by the root user. This can really screw things up for you if you try to do npm <something> in the same directory or project later on.
  • Even sudo npm install -g with a valid installation target can mess things up for you and make it hard to use npm without sudo under some circumstances in the future -- particularly if you change your npm configuration midstream. The root user can and will create files in your npm cache and potentially a file like ~/.npm/_locks, and future npm install or npm install -g will give you the dreaded EACCES error.

So when it comes to using sudo with npm: just don't do it.

npm install -g for yourself

Most of the time you will be probably be working on a system that only requires a single user to use node and globally installed binaries (yourself on your own machine, some node user on servers). The simplest solution for the npm install -g problem is to simply change where the node modules are installed.

Explicit prefix

npm uses the prefix setting to determine where to install globally -- or at least what it calls globally. You can see what the prefix is set to by running npm prefix -g, and it's probably something like /usr. This is undesirable. Instead, it would be nice to globally install node modules to a directory the current user has access to.

npm --prefix=/home/your-user/.global-node-modules install -g grunt-cli

Of course you can change the prefix to whatever you want. It would also be a tremendous pain to have to type out this --prefix option every time, so fortunately there exists a .npmrc file that npm will use to check defaults. Mine looks like this:

# ~/.npmrc tmp=/home/ajcrites/files/node-tmp cache=/home/ajcrites/.npmcache prefix=/home/ajcrites/.npm

You can pick any values you want of course. A full list of all of the config settings you can apply to the npm command or set in your .npmrc is listed with npm help 7 config (that took some digging to find).

At any rate, once you pick some nice, out-of-the-way hidden folders, npm install -g will stop putting all kinds of garbage in your home directory, /usr directory, and various other spots when you run npm install -g (and npm install in some cases).

Unfortunately, there doesn’t seem to be a configuration setting for where to put npm-debug.log ... yet.

In summary, setting prefix in .npmrc or simply using the --prefix will allow you to use npm install -g without sudo.

But wait! You also have to make sure that the binaries are on your path. Simply add the $PREFIX/bin to your path. So in my case:

# .zshrc / .bashrc / .profile / etc. export PATH=$PATH:$HOME/.npm/bin export NODE_PATH=$NODE_PATH:$HOME/.npm/lib/node_modules

Note that the setting of NODE_PATH will cause node to check this path for libraries. More information is here, and this may or may not always be desirable. I've just included it for completeness.

Using nvm

Setting up .npmrc, and $PATH can be a lot of work. Well, not really, but imagine that it is.

Also keep in mind that your current version of npm/node matters. Some libraries may only support or enforce a requirement of v0.10 whereas you may be running v0.12 on your system.

nvm is an awesome package that requires very little setup and allows you to easily install and switch between node versions. You can even add nvm use <specific-version> to your profile if you're doing a lot of work with a particular version and you want to use it any time you start a shell.

So why is this great? nvm updates your prefix! -- at least if you haven't already set one. It will install binaries to ~/.nvm/<version>/bin. It adds this directory to your $PATH when you run nvm use too! And if you switch back to nvm use system or some other version, it removes it appropriately.

So just keep in mind that binaries installed after nvm use will only be usable when you do the same nvm use again (unless you update your path to include them explicitly).

Note that nvm only does this if you don’t have prefix set in your .npmrc. Of course, you can still override everything with npm --prefix. If you have prefix in your .npmrc or use --prefix, then npm install -g used after nvm use will still use your prefix settings. I think that this is usually a good thing.

I’ve run into tiny problems with nvm not being sourced properly. You just need to do source /path/to/nvm/nvm.sh. nvm tries to add this to your profile automatically, but it may not always work as expected. Update .zshrc, .bashrc, others as needed.

npm install -g for a server

The previous section is all perfectly applicable for a server where you are deploying a node app. Usually your server will have a user (call it node or whatever you want) who is in charge of running node. Set up their .npmrc or just set up your build jobs to use --prefix as appropriate.

This is still not a use case for sudo npm install.

npm install -g for all users

There may be occassion for a system to allow multiple users to globally install and use node package binaries and libraries. There is still no reason to use sudo to do this -- at least not for the npm command.

My solution for this would involve creating a directory to install global node modules to — perhaps in /var, although /usr could be valid ... I'm still wary of this, though since there are non-node things in /usr/bin.

sudo addgroup npm-global-installers sudo mkdir -p /usr/{bin,lib/node_modules} sudo chgrp -R npm-global-installers !$ sudo chmod -R g+w !$

!$ above is history expansion for "last word of the previous command," or /usr/{bin,lib/node_modules} in both cases.

This creates a group that can run npm install -g to add node modules to /usr/lib. You can add trusted users to this group on your system and do a true global installation of node modules.

However a major caveat to this solution is that anyone in npm-global-installers can clobber the global installs of others. Individuals can solve this problem simply by using their own prefix, but this defeats the purpose of the global installation.

Another solution is to simply have an npm-global-intaller user and update his prefix to ~/npm -- then have everyone add ~npm-global-installer/npm to their $PATH. Other users can do these global installations using sudo with the npm-global-installer user (not root -- I haven't tested this and it still may make some ~/.npm/_locks that you don't have permissions on) or this directory can be made group/world writable.

Both of the above solutions are viable for allowing multiple users to use node package binaries / libraries system-wide.

Of course if you really want to use another user’s installed libraries you can update your $NODE_PATH to include it. If you want to use a binary, update your $PATH to include it, or even just do /home/other-user/path/to/node/bin/script (assuming you can execute it).

I’ve already run sudo npm install. Help!

If you’re running into weird errors with npm install -- particularly things that say EACCES a lot after you've done sudo npm install in the past it's most likely a permissions issue on the directories that npm is trying to alter. This is a consequence of npm being dumb in a good way. It will happily try to do what you tell it to do and create files and directories that have root permissions or try to alter these when you have no such permissions.

That being said, once you need to alter a file created by sudo npm install you must use privileges to either change its permissions or remove it entirely.

The simplest solution is to do sudo rm -rf node_modules for whatever project you are currently in. Similarly, you may have to do something akin to sudo rm -rf $(npm prefix -g)/{bin,lib/node_modules} in case you globally installed node modules with the wrong prefix. Just be aware that this will remove libraries you installed with sudo before, so you will have to install them again. The right way. Consider this your comeuppance.

More specifically pay attention to the output. Read what npm is telling you and find the directory that is causing a problem specifically. Remove it. If you can't remove it, you'll have to use sudo rm.

Ultimately you should be able to do an npm install or npm install -g without using sudo.

Sometimes you may run into other unrelated issues, though (give npm install oracledb a try!)

Know what you are doing with sudo

I get the idea that a lot of developers learn the lesson

If something doesn’t work, try it again with sudo.

This reminds me of a similar circumstance: if kill doesn't kill a process, use kill -9. I think that you could ask a lot of developers which signals kill and kill -9 send to processes and many would give you a blank stare in response (SIGTERM AND SIGKILL).

In the same vein, using sudo is not an answer for everything. There's a reason why root is called a privileged user. You earn privileges by exercising responsibility, and chief among those responsibilities is knowing what a command is going to do when you actually run it.

Unless you know exactly what a command will do before running it with sudo (or you don't care about screwing up the system you're on) ask someone. sudo is something that should be used with care, not with frustrated abandon. For that matter, know that visudo and sudoedit exist as well!

If you take anything away from this post, though, it should be that you never need to do sudo npm for anything.

It’s pronounced “sue doo” by the way.

Originally published at blog.explosion-pills.com on April 9, 2015.

--

--