Don’t use `sudo` with `npm`
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 hownpm
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 donpm <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 usenpm
withoutsudo
under some circumstances in the future -- particularly if you change yournpm
configuration midstream. Theroot
user can and will create files in your npm cache and potentially a file like~/.npm/_locks
, and futurenpm install
ornpm install -g
will give you the dreadedEACCES
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.