Install NPM into home directory with distribution nodejs package (Ubuntu)
node.jsNpmnode.js Problem Overview
I'd like to use the distribution Node.js packages (or the chris-lea ppa for more recent releases) but install NPM to my home directory.
This may seem picky, but it's a pretty idiomatic way for polyglot/github-using developers to setup language runtime/library environments under Linux: distro packages for the runtime, 3rd-party libraries in per-user environment (see virtualenv, RVM - RVM will also build Ruby for you if you want). If necessary I will build node locally but it's a PITA since Node is becoming an incidental development requirement for lots of projects.
node.js Solutions
Solution 1 - node.js
NPM will install local packages into your projects already, but I still like to keep the system away from my operating system's files. Here's how I suggest compartmentalizing Nodejs packages:
Install Nodejs and NPM via the chris-lea PPA. Then I set up a package root in my homedir to hold the Node "global" packages:
$ NPM_PACKAGES="$HOME/.npm-packages"
$ mkdir -p "$NPM_PACKAGES"
Set NPM to use this directory for its global package installs:
$ echo "prefix = $NPM_PACKAGES" >> ~/.npmrc
Configure your PATH and MANPATH to see commands in your $NPM_PACKAGES prefix by adding the following to your .zshrc/.bashrc:
# NPM packages in homedir
NPM_PACKAGES="$HOME/.npm-packages"
# Tell our environment about user-installed node tools
PATH="$NPM_PACKAGES/bin:$PATH"
# Unset manpath so we can inherit from /etc/manpath via the `manpath` command
unset MANPATH # delete if you already modified MANPATH elsewhere in your configuration
MANPATH="$NPM_PACKAGES/share/man:$(manpath)"
# Tell Node about these packages
NODE_PATH="$NPM_PACKAGES/lib/node_modules:$NODE_PATH"
Now when you do an npm install -g
, NPM will install the libraries into ~/.npm-packages/lib/node_modules
, and link executable tools into ~/.npm-packages/bin
, which is in your PATH
.
Just use npm install -g
as you would normally:
[justjake@marathon:~] $ npm install -g coffee-script
... (npm downloads stuff) ...
/home/justjake/.npm-packages/bin/coffee -> /home/justjake/.npm-packages/lib/node_modules/coffee-script/bin/coffee
/home/justjake/.npm-packages/bin/cake -> /home/justjake/.npm-packages/lib/node_modules/coffee-script/bin/cake
coffee-script@1.3.3 /home/justjake/.npm-packages/lib/node_modules/coffee-script
[justjake@marathon:~] $ which coffee
/home/justjake/.npm-packages/bin/coffee
Solution 2 - node.js
Jake's answer was posted in 2012 and while useful it references Chris Lea's Node.js PPAs who are no longer updated since march 2015.
Here's the steps I use to install Node.js and npm in my home directory:
nvm (no sudo
required):
Install Node.js with curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash
source ~/.bashrc
nvm install 7
npm install -g npm # update npm
Now you can install -g
without sudo
and everything goes into ~/.nvm/
official instructions):
Or install Node.js without nvm (Install Node.js
-
Node.js v6 (current LTS as of May 2017):
curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash - sudo apt-get install -y nodejs
-
Node.js v7:
curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash - sudo apt-get install -y nodejs
Change npm's default directory to a local one:
mkdir ~/.npm-global
npm config set prefix '~/.npm-global'
export PATH="$HOME/.npm-global/bin:$PATH" # ← put this line in .bashrc
source ~/.bashrc # if you only updated .bashrc
Alternatively replace .npm-global
by the directory of your choice.
$HOME
directory:
Update npm and check it is installed in your $ npm install npm -g
/home/<username>/.npm-global/bin/npm -> /home/<username>/.npm-global/lib/node_modules/npm/bin/npm-cli.js
/home/<username>/.npm-global/lib
└─┬ npm@3.10.6
├─┬ glob@7.0.5
│ └── minimatch@3.0.2
├── npm-user-validate@0.1.5
└── rimraf@2.5.3
Now you can install -g
without sudo
and without messing with your system files.
Solution 3 - node.js
The solution posted by Just Jake is great. However, due to a bug with npm > 1.4.10, it may not work as expected. (See this and this)
While the bug is solved, you can downgrade to npm 1.4.10 by following this steps:
- Comment the prefix line in your $HOME/.npmrc
- Run
sudo npm install -g [email protected]
- Ensure that the right version of npm is installed (
npm --version
) - Uncomment the prefix line in your $HOME/.npmrc
- Proceed to install your global packages in your home folder!.
Solution 4 - node.js
Because python does already a great job virtualenv
, I use nodeenv
. Compared to nvm
, you can create multiple environments for the same node version (e.g. two environments for node 0.10
but with different sets of packages).
ENVNAME=dev1
# create an environment
python -m virtualenv ${ENVNAME}
# switch to the newly created env
source ${ENVNAME}/bin/activate
# install nodeenv
pip install nodeenv
# install system's node into virtualenv
nodeenv --node=system --python-virtualenv
The readme is pretty good: https://github.com/ekalinin/nodeenv
Solution 5 - node.js
I used @just-jake solution for some time and found that nvm is easier to setup.
Also it's much powerful solution that allows to install and use different versions of nodejs
.
On Ubuntu 14.04 or 16.04:
-
Install prerequisite packages for building
nodejs
:sudo apt-get update sudo apt-get install build-essential libssl-dev
-
Install nvm:
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.1/install.sh | bash
In case newer version of nvm will be available you can find actual installation command on nvm site.
-
nvm
installer will add bootstrap script to~/.bashrc
, so you need either to reopen terminal to run it, or to do:source ~/.bashrc
-
Now you can install any
nodejs
version you like, switch between them etc.Use
nvm ls-remote
to list availablenodejs
versions.To install, for example,
nodejs
v4.2.4 do:# install v4.2.4 nvm install v4.2.4 # use nodejs v4.2.4 in the current terminal session nvm use v4.2.4 # use v4.2.4 by default in new terminal session nvm alias default v4.2.4
Solution 6 - node.js
As stated already here and here
npm config set prefix ~
echo export PATH=\$PATH:\~/bin >> ~/.bashrc
. ~/.bashrc
Solution 7 - node.js
Other answers have outdated solutions: 2020's solution is using NPM_CONFIG_PREFIX
environment variable. (See details)
For example,
$ NPM_CONFIG_PREFIX="$HOME/.npm-packages" npm install -g ios-sim
/Users/<name>/.npm-packages/bin/ios-sim -> /Users/<name>/.npm-packages/lib/node_modules/ios-sim/bin/ios-sim
+ ios-sim@9.0.0
added 108 packages from 68 contributors in 3.094s
Solution 8 - node.js
To expand on the answer provided by Just Jake and user1533401: I am unable to downgrade as I use shared hosting and node is installed in a system directory. This is also why I have change the directory where npm installs global scripts if I want it to do that. For those in the same boat, here is a another temporary fix I found works:
npm install -g --prefix=$(npm config get prefix) <package>
The bug is that npm doesn't read your per-user config file, but specifying it every time you install a global script fixes that. Found here.
Solution 9 - node.js
I have a slightly different solution to a similar problem, which was due to my installing npm
globally so I can use it in the Terminal of my macOS system. I simply initialised it locally at the root directory of my repository with the command:
npm init --yes
This did the trick for enabling me to install node packages in the local root directory at /node_modules with the "package.json" and "package-lock.json" files instead of at the user's home directory.
Solution 10 - node.js
At least on Ubuntu the default config for system wide npm
is that npm install --global
tries to install packages to /usr/lib/node_modules
. To set different default for your own user account run following once:
mkdir -p ~/.npm/lib/bin
npm config set prefix "~/.npm/lib"
in addition you want following fragment in .profile
:
# set PATH so it includes user's private .npm/lib/bin if it exists
if [ -d "$HOME/.npm/lib/bin" ] ; then
PATH="$HOME/.npm/lib/bin:$PATH"
fi
If you now install something with npm install --global packagename
it will end up in correct location and can be found in your PATH (you may need to logout and re-login for .profile
changes to take effect).
Of course, you could select some other directory instead. For example ~/.config/npm
could make sense for modern systems.
Solution 11 - node.js
You can use npm-user
to automatically set up npm
to install packages into your user's directories instead of the system's. No root
privileges needed.
Here's a link to the script, instructions on how to use it and information about its options. It works on macOS, Linux, *BSD and Windows.
You can run it like so:
$ curl -s "https://raw.githubusercontent.com/alexdelorenzo/npm-user/main/npm-user.sh" | bash
After you run it, using npm install -g <package>
will install packages to your user's directories without needing to use sudo
.
Here's the code if you want to copy and paste it into your console:
#!/usr/bin/env bash
# Copyright 2022 Alex DeLorenzo <alexdelorenzo.dev>. Licensed under the GPLv3.
export ROOT="${1:-$HOME}"
export NPM_DIR=".npm-packages"
export NPM_ROOT="$ROOT/$NPM_DIR"
export NPM_BIN="$NPM_ROOT/bin"
export NPM_MAN="$NPM_ROOT/share/man"
export BASH_RC="$HOME/.bashrc"
export ZSH_RC="$HOME/.zshrc"
export DEFAULT_RC="$BASH_RC"
export RC_ERR=1
export INDENT=2
set -e
shopt -s expand_aliases
alias indent="paste /dev/null - | expand -$INDENT"
quiet() {
"$@" &> /dev/null
}
expand-tilde() {
local path="$1"
echo "${path/#\~/$HOME}"
}
create-paths() {
local bin="${1:-$NPM_BIN}"
local man="${2:-$NPM_MAN}"
mkdir --parents --verbose "$bin" "$man"
}
set-prefix() {
npm config set prefix "$NPM_ROOT"
}
get-vars() {
local bin="${1:-$NPM_BIN}"
local man="${2:-$NPM_MAN}"
cat <<EOF
export PATH="\$PATH:$bin"
export MANPATH="\${MANPATH:-\$(manpath)}:$man"
export NPM_PACKAGES="$NPM_ROOT"
EOF
}
already-added() {
local rc="${1:-$DEFAULT_RC}"
local bin="${2:-$NPM_BIN}"
local man="${2:-$NPM_MAN}"
local vars="$(get-vars "$bin" "$man")"
quiet grep "$vars" "$rc"
}
main() {
local rc="$(expand-tilde "${1:-$DEFAULT_RC}")"
local bin="$(expand-tilde "${2:-$NPM_BIN}")"
local man="$(expand-tilde "${3:-$NPM_MAN}")"
printf "Creating %s and %s\n" "$bin" "$man"
create-paths "$bin" "$man" || {
printf "Couldn't create paths: %s and %s.\n" "$bin" "$man"
return $RC_ERR
}
printf "Setting npm prefix.\n"
set-prefix || {
printf "Couldn't set prefix.\n"
return $RC_ERR
}
if ! already-added "$rc" "$bin" "$man"; then
printf "Writing to %s.\n" "$rc"
get-vars "$bin" "$man" >> "$rc"
fi || {
printf "Unable to write to %s.\n" "$rc"
printf "Add the following to your shell's configuration file:\n\n"
get-vars "$bin" "$man" | indent
return $RC_ERR
}
printf "Done.\n\n"
printf "To load the changes in this shell, run:\n"
printf "\tsource %s\n" "$rc"
}
main "$2" "$3" "$4"