Don’t Use `sudo` with `npm` …still
--
This is a rehash of an article I wrote in 2015 about how you shouldn’t use the sudo
command with npm. That is to say, you should never do sudo npm
… anything. I feel that this needs a bit of a rehash since I’ve learned some new techniques for managing Node.js and global modules on my development machine. The advice here also applies universally, but the specific examples are tailored to local development on OS X. Most of the examples should work on any popular OS with minor modifications.
Development machines, production machines, or machines from any environment shouldn’t be using sudo npm
or sudo yarn
(and probably shouldn’t be using sudo
at all, but that’s another story).
sudo npm install
(and sudo npm $ANYTHING
) is still a bad idea ™ for the at least following reasons:
npm install
and others have 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.
This remains true even with new security measures added to npm
. When it comes to using sudo
with npm
, Just don’t do it!
Avoid Global Installs
I’ve found it favorable to avoid global installations altogether in favor of project-level installations. This is made even more convenient with the addition of npx
which will run a node module from the registry without permanently installing it to your system. So instead of running npm install -g react-native && react-native init
you can do npx react-native init
.
This will create a project that will have node_modules/.bin/react-native
, so as long as you’re in said project you can use npx react-native
or yarn react-native
to actually run the locally-installed react-native
command without having it installed globally. This also applies to tools like gulp
and grunt
and pretty much any other such tool you can think of.
Note: for whatever reason, Ionic and Cordova only seem to work properly as global installs for now.
If you were to run react-native init
a lot, it would get annoying to have to install it with npx
every single time you needed it. In this case, installing a global module makes sense. Let’s look at how to do that without sudo
.
Set npm’s Prefix
npm
has a configuration called prefix
. This configuration setting tells npm
where to install global modules (yarn
also uses it). For more information on this configuration setting and others, see npm help 7 config
.
You can set this per-install using npm --prefix=$PREFIX
, but most likely you will want all of your global modules installed to the same place each time. You can update your global node configuration to do this. I did mine using npm config set prefix ~/.npm
. This is equivalent to editing your ~/.npmrc
to include the line prefix=~/.npm
. You can make the prefix whatever you like, but it should be in a directory that your user has permissions to write to. That should be anything under your home directory, ~
.
But wait! You also have to include modules installed here to your PATH
. In your .zshrc
, .profile
, or equivalent you should also set something like export PATH="$HOME/.npm/bin:$PATH"
. This will allow you to run globally installed modules that are located in ~/.npm/bin
.
You can also configure yarn to use its own prefix if you like, but for me I have to swap between using yarn
and npm
for quite a few projects. It’s nice to have one configuration to worry about.
Using `n` to Manage Node.js Versions
My previous version of the article included instructions for using nvm
. It’s a great tool, and you should use it if you wish, but I prefer TJ Holowaychuk’s n
library for this purpose now.
Once you have installed a system-wide version of node, e.g. with brew install node
, you should have access to npm
and yarn
. Now you can set your prefix and PATH
. Once that’s done, you can run yarn global add n
or equivalent. You should now have access to the n
executable. which n
should confirm this.
Now you’ll also have to update your N_PREFIX
since n
tries to install the versions it manages to /usr/local
by default. I think that Homebrew suggests running sudo chown -R $(whoami) $(brew --prefix)/*
or something like that, but I’m not a huge fan of doing this. I’d rather manage things in my home directory.
You can add export N_PREFIX=$HOME/.n
or whatever you like. You must also add this to your path, for example: export PATH="$PATH:$N_PREFIX/bin"
. Now you can use n latest
to install the latest version of node.js to a directory that you control.
Summary
To summarize, you can use the following steps to update your local OS X machine to allow you to use global Node.js modules and different versions of node without using sudo
.
brew install node
, orapt-get install node
or whatever you need to do to get node up on your machine. This should include executablesnpm
andyarn
.- Set your prefix for global installs, e.g.
npm config set prefix ~/.npm
- Update your PATH to include
~/.npm/bin
. For example:echo 'export PATH="$HOME/.npm/bin:$PATH"' >> ~/.zshrc
if you’re using zsh.
At this point you’re good to go for installing global modules. I think it’s good to avoid installing global modules except for commands that you will use frequently that won’t be installed to a corresponding project. For one-off commands such as initiating projects that you will only do every once in a while, you can use npx
.
- Next, use
yarn global add n
which should create~/.npm/bin/n
. - Do the equivalent of
echo 'export N_PREFIX="$HOME/.n"' >> ~/.zshrc
for your shell configuration
Now you can run n
to install different versions of node to a directory that you control.
On build / production environments you should avoid global installs of modules as they are generally not needed. If they’re required, you can still do a similar setup with a user who only has permissions to install global node modules to a particular directory using --prefix
and so on.
Original Article (includes more information on working with other kinds of environments and using nvm
): https://medium.com/@ExplosionPills/dont-use-sudo-with-npm-5711d2726aa3