Nvm: Documenting how to install NVM for all users

Created on 22 May 2017  Â·  18Comments  Â·  Source: nvm-sh/nvm

I needed a global install of NVM because I have some node based cron jobs and a few legacy applications that are rather picky about which version of node they are able to work on. For a time I worked around this by sourcing the nvm script everywhere but that seems to be a somewhat unmaintainable solution.

The requirements I have:

  • NVM should just work in non-interactive sessions
  • Every user should be able to select a installed version of node (or use $ nvm exec)
  • Some users should be able to install newer versions of node

This is the solution I came up with: (shout out to @icecoldphp, for the initial version)

  1. I've done this on a debian 8 machine, as the root user
  2. Create a group called "nvm", # groupadd nvm
  3. Add root to the nvm group # usermod -aG nvm root
  4. Goto the /opt directory and create a directory called nvm

    • Make sure the groupd owner is nvm # chown :nvm ./nvm

    • Set the permissions so that the group is allowed to write in there and all file will inherit the group # chmod g+ws ./nvm

  5. Follow the git install steps using /opt/nvm as the directory

    • To make sure the group can also write aliases, cache downloads and install global packages make sure the directories exist and have the correct permissions:
    # mkdir /opt/nvm/.cache
    # mkdir /opt/nvm/versions
    # mkdir /opt/nvm/alias 
    
    # chmod -R g+ws /opt/nvm/.cache
    # chmod -R g+ws /opt/nvm/versions
    # chmod -R g+ws /opt/nvm/alias
    
  6. Using the following snippet create /etc/profile.d/nvm.sh:
    ```#/etc/profile.d/nvm.sh

    !/bin/bash

    export NVM_DIR="/opt/nvm"
    [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"

    ```

  7. Ensure that the script is executable # chmod +x /etc/profile.d/nvm.sh
  8. If you want to use nvm in non-interactive sessions as well make sure to source the nvm file in /etc/bash.bashrc before the line saying # If not running interactively, don't do anything by adding . /etc/profile.d/nvm.sh.
  9. For bash completion (which is inherently interactive ;) add [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" after the section about bash completion.

Every user can select a version of node (as the permissions for public are r-x) and users in the nvm group can install and remove versions of node (permissions for the group are rwx).

My questions are:

  • As a developer I know next to nothing about linux, could this be improved, is it bad style, etc? Any feedback is welcome.
  • Should this be documented in the NVM README.md?
root / multiuser issues

Most helpful comment

There is of course n (with n-install) which with a coaching could do the same. I'll give that a shot and create a gist of the process, what are the possible other tools, other than say apt-get, brew or some system level package manager which you are usable to manager the version of node in your shell?

All 18 comments

nvm is not intended to be global or system-wide - it's per-user, per-shell-session.

Thus, each user account must have its own $NVM_DIR. They can certainly share an nvm.sh, but I'd recommend they all have their own one of those too.

I would not want to document anything in the readme that encourages people to use nvm across user accounts - there's other tools for that.

There is of course n (with n-install) which with a coaching could do the same. I'll give that a shot and create a gist of the process, what are the possible other tools, other than say apt-get, brew or some system level package manager which you are usable to manager the version of node in your shell?

Yes, n is the sole system-wide node manager I'd recommend.

However, I'd suggest just installing nvm in the cronjob user, and invoking the cronjobs such that nvm.sh is sourced.

Yes it is always good to go with what nvm is intended for. per user. Go for per user installation. I have a similar kind of a situation (I used to install node without nvm previously) and did the same.

nvm is not intended to be global or system-wide - it's per-user, per-shell-session.

You know, for an util that should eliminate version discrepancies and staff it sure does increase it a lot... I mean I have server that deploys web projects on git pushes via hooks (i.e user = git). But sometimes I need to log in and redeploy the same things manually by invoking the git hooks manually (user = me). And sometimes my colleagues have to do the same (user = foo)... And then there is a process manager (PM2) that should be central for everyone, but it relies on node as well...

And everybody have it's own node&npm. Except root, so trying to sudo yields even less results, i.e node: command not found. Ok, your util is the wrong one for these kind of things, but NPM in it's official docs explicitely says to use nvm to avoid permission problems that unavoidably accompany multiuser usage.

Yes - the hazard is "multiuser usage". In your use case, everyone should be running node as the same user.

I'm writing a PHP wrapper for a NodeJS binary and I have this exact same problem. PHP is running as "www" so it has no home directory.

PHP can't "see" the NVM environment.

I had this exact same problem with getting node running in crontab but at least there I could control the user to be anything.


Thank you @AndreSteenveld

This is yet another example of open-source project maintainer arrogance. Your use case doesn't matter so jump through these hoops.

@hparadiz that's pretty hostile. I'm not being arrogant here, nor am I saying the use case doesn't matter - I'm saying that this project explicitly does not support this use case. "Arrogance" would be assuming that free labor on an open source project you don't pay for is obligated to support your use case.

Literally didn't even call anyone out by name other than the one person who helped and I THANKED them. This issue is still a problem. Hasn't been closed. @Spown makes a good point too.

Yes, it is a project maintainer's hubris to dismiss people's use cases as invalid. Heck, I do it all the time. Just cause something is free doesn't excuse you from doing things people don't like. So it's free? I'm not allowed to complain about a vital feature being missing? I guess that makes me hostile. But remember for everyone 1 of me who bothered to write something here there's 99 others that find this page, get super annoyed, and move on with their day deciding on one solution or another.

People want to use node, nvm, and npm system-wide in a global context. This isn't rocket science. Why do people have to install it manually for every user on a system?

Like would it really kill you make a link in /usr/sbin and check the user that ran the command?

Congrats your tool is now an instrumental part of NodeJS development. It's time to level up and make it a system-wide command.

@hparadiz I would prefer people with that desire and use case to use a different tool, since that's not what nvm is designed for. Just because you want to cut down a tree doesn't mean a pocket knife is the right tool for the job.

Hey guys,
A bit of heresy from me over here that relates to this thread.
(but let's not launch the Spanish Inquisition (tm) just yet)
We needed properly managed and stable node version on our bespoke in-team-dev machines as well as on some specific live use-cases, and for that have forged an Ansible role to do just that. You may call it a hack, or you may not, it's your free will, but we're using NVM as the drop in replacement for the distro's node package, accessible globally.
NVM turned out to be heavens apart in terms of stability than any apt + n combination we tried. I appreciate the tool (kudos and big thanks to you @ljharb) so it's the solution we incorporated.
For anyone interested (hope you don't mind sharing here bro):
https://github.com/grzegorznowak/ansible-nvm-node

For anyone else, please use the proper user-scoped approach. Whatever floats your servers chaps.

This problem exists when running node via cron as well since cron doesn't source any bash scripts and therefore won't see node installed via nvm.

The solution for now tends to be to source the nvm bootstrap before every cron command:
* * * * * source .nvmrc; node /my/script.js

See: https://unix.stackexchange.com/questions/67940/cron-ignores-variables-defined-in-bashrc-and-bash-profile

The _real_ solution is still to install node globally but what do I know? I'm just a silly user.

@hparadiz there's no need for the passive-aggressive comments. If you want to install node globally, go for it - but nvm isn't the right tool for that job.

@hparadiz , cron is notorious for that. Be it node or not. You would want to put in full paths to whatever binary you get to use inside a cron.
Thanks to your question, I just realized cron doesn't even see /usr/local/bin, so will update my role to use the most common of paths: /usr/bin/node, and if you did similarly yourself then you could use the absolute path to your gulps etc, like:
/var/lib/nvm/versions/node/v8.11.3/bin/gulp
and the hack would be completed, I think.

Your assertion that nvm is the wrong tool for the job is a slap in the face of the paradigm created by BSD systems for the past three decades.

If I install a binary on a machine it's expected that the binary is available to the entire system. Not just a single user. If I install ffmpeg on a machine I expect that I can run ffmpeg from my terminal but also from any running process. Expecting a process to source a bash script for every binary is madness. If everyone followed your paradigm the *nix eco system would be way worse off.

Here's documentation on BSD directory structure. Here's a pretty good discussion on where user level packages should be installed. Sadly nvm follows none of these well documented installation paradigms. If you did we wouldn't be having this discussion.

Furthermore if there are actually two users on a machine that need to use nvm you are duplicating binaries on-disk for each user for absolutely no reason. You could easily store them globally and link them to the user's home directory should you choose to continue to use your existing directory structure. My nvm folder on my Macbook Pro is already 479M with only two versions of node. If I told every developer to install node via nvm for every user I'd be looking at gigabytes of files on-disk for absolutely no reason. I tend to like efficiency and this is not it.

The only reason people use nvm is because node's release schedule is very rapid and apt, yum, brew, and other built in system package managers choose to not update fast enough. For this reason you have people using nvm even though they need node installed globally on a machine.

Which brings me to my next point: node is a run-time binary for running Javascript code. A run-time _should_ be installed globally. This is how literally every single run-time works.

By default when someone claims to have a piece of software that is a "version manager" I would expect it to do that on my system, not just within my bash terminal.

You might as well change the description of this project to:

Node Version Manager - Simple bash script to manage multiple active node.js versions (for the current terminal user in bash compatible shells only).

You have multiple people in this thread asking for this and you claim that nvm isn't designed for this. Which is odd since nvm is obviously downloading node to a directory on the user's machine and managing directory links and aliases for them. Having it be global is a tiny additional feature on top of what you already have. You could simply soft link the directory where node is installed to /usr/local/ and be good to go (if root).

Finally for security reasons Linux explicitly supports users (section 6) without a home directory. A common one in Ubuntu is www-data for web servers. In this thread you are explicitly saying you won't support this completely valid use case.

I'm not trying to be mean or passive aggressive. What you're sensing is disappointment.

@grzegorznowak
Yes, cron is notorious but that's only because most people have no idea that the cron running environment is completely different from their terminal bash environment.

Make it tell you what's going on:

* * * * * (source ~/.bash_profile; node -v; date) >> ~/crontest.log
$ tail -f ~/crontest.log 
v8.4.0
Wed Sep 26 20:11:01 EDT 2018

You actually do not want full paths to the binaries in your crontab. Use env.

You can actually make your JS files themselves executable!

printf '#!/usr/bin/env node\nconsole.log(process);' > myscript.js
chmod +x myscript.js
./myscript.js

And yea... it's pretty dope.
image

tldr; make your crons executables themselves and use env

@hparadiz that's not actually how BSD systems work. Installing a binary to a location provides the binary only if it's available in the PATH, and the PATH can be set per-user, or per shell session, so in fact contextual binary usage is the predominant paradigm - assuming that a binary is global everywhere is a slap in the face to how BSD systems work, and shows your ignorance of the same.

That nvm may not follow some conventions is irrelevant; they're conventions, not requirements.

I don't agree that a runtime should be installed globally, nor that it is. Many binaries are installed per-user.

That multiple people are asking for (free labor) doesn't mean they're entitled to it; I continue to claim that if you want this feature, YOU SHOULD NOT USE NVM FOR IT. That you're unhappy with that isn't really my concern.

If you're disappointed, I'm sorry for that - but there's no need to take that out on me, or this thread.

Please see https://github.com/creationix/nvm/issues/1533#issuecomment-303203372 if you have any questions - nvm is not designed for, or intended for, multiple users, and will never support the same.

This issue remains open because there's clearly some documentation change that could be made in the readme to make this more clear. A PR to do so is welcome.

Was this page helpful?
0 / 5 - 0 ratings