Nvm: How to use nvm for production services

Created on 28 Apr 2015  路  16Comments  路  Source: nvm-sh/nvm

We would like to use nvm to run multiple applications on our qa and production services in multiple node versions. I had envisioned each project having an .nvmrc file and simply runnng "nvm use" as part of the service init script. However, since nvm is written as a function, and needs to be sourced into your login profile, this does not appear to be a workable option. Init scripts do not have a login shell or profile, so I am unable to get access to any of the NVM commands.

Is there an alternate approach, am I missing something, or is NVM not recommended for running production services?

root / multiuser issues

Most helpful comment

For anyone else landing here from google, here is a full systemd service definition:

[Unit]
Description=example.com
After=network.target

[Service]
Environment=NODE_VERSION=6.9.1
Type=simple
User=linus
ExecStart=/home/linus/.nvm/nvm-exec node .
Restart=on-failure
WorkingDirectory=/home/linus/example.com

[Install]
WantedBy=multi-user.target

You want to replace:

  • User=linus with the user you want running the service
  • ExecStart=/home/linus/.nvm/nvm-exec with the path to your installation of Nvm
  • WorkingDirectory=/home/linus/example.com with the directory where your Node.js app is installed

All 16 comments

nvm only works in a shell session, and is installed for a single user. If your environment won't be launching node from a shell that can source nvm, then you're correct that nvm won't work for your use case (I use node and nvm in production from a shell, so it works for me).

Since you need multiple node versions, https://www.npmjs.com/package/n won't work either - that's just one systemwide - and nave requires launching a subshell.

I'm not really sure how you'll be able to manage multiple node versions without a shell environment. I'll leave this open for a few days in case you or someone else has an idea of how I could improve nvm to work for your use case.

I shelved this project for a little while, and recently came back to it. We have a working prototype using https://github.com/ryuone/nenv but I'd prefer to use nvm if possible.

@ljharb can you provide some insight to how you're running applications in production with NVM? What does the startup process look like? How do you monitor that your applications stay runing and/or restart after crashes or system reboots?

@nheath usually a service runs nvm run node path/to/script.js or nvm exec node forever path/to/script.js or something similar. It depends on the system. I don't manage the monitoring tools, but there's lots of standard ones that should work.

@ljharb thanks--What I meant is, what service (that has a shell/profile) runs that command? Are you using upstart, sysv, systemd, or something else?

before I handed things over to real ops people, it was just an @reboot cron entry :-p don't hate me

I use upstart and nvm in production. I simply use nvm to install the binaries and then either set the custom path manually in my upstart script or use the full path to the node version I want.
Setting the custom $PATH in upstart is best since many things also need $HOME and may use other node scripts in the custom nvm bin folder.

That works fine - it's just using "root" as the user that nvm/node runs under. You can also be safer, and pick any non-privileged user you like.

I use ndenv for cases like this. nvm/rvm's approach of injecting shell functions is inappropriate for automation, because some processes simply won't invoke your target tool through the shell (or a properly configured shell). For example, see the problems that React Native (especially the fixed issues ref'ed in that PR) is having because it's heavily recommended the use of nvm. They shipped a versions that tries to invoke the npm binstub from the react-native-cli package and everything broke for nvm users. It looks like they're going to hack in an explicit attempt to find and load nvm in their Xcode build script to work around this problem.

I hold out scant hope for this request, but I'd love to see nvm refactor/rewrite to use ndenv's approach, augmented with the more "rounded corners" of nvm's current overall UX. ndenv (and rbenv, its Ruby-side progenitor) just leverages PATH to do its work. When an npm binstub is installed, a corresponding shim script is created in ~/.ndenv/shims, which is the directory on the user's PATH. When an npm-installed command-line tool is called, e.g. react-native the shim is what's invoked. The shim asks ndenv for the correct node to use based on the usual policy (.node-version file, user's global setting, etc.), then proxies off to the correct node environment.

This approach is highly amenable to automation since it leverages Unix's core principles around PATH. I've successfully deployed this with both ndenv/rbenv to manage node/ruby versions in complex dev-to-production and devops VM environments, build environments, etc.

https://github.com/riywo/ndenv and https://github.com/ryuone/nenv seem similar up to identical. @jwhitley @nheath, have you experience with these?
Thanks!

@elektronik2k5 I've used ndenv, which works well. It supports installation of upstream node binary packages, but doesn't build from source. I'm not familiar with nenv, but it uses a shims directory on PATH just like ndenv does, which is the important part for integration with other tooling. Both inherit this strategy from rbenv, which I've used extensively in production environments (e.g. with provisioners like Puppet & Chef, in both VM environments and on EC2 & similar.)

If you need a single system-wide node version, I'd recommend https://www.npmjs.com/package/n. n, nvm, and nave are the three most-used node version managers by a large margin.

I presume that this issue was solved with nvm-exec. This fragment of a systemd configuration appears to work for me, although I haven't yet tried it in production:

Environment=NODE_VERSION=7
ExecStart=/opt/nvm/nvm-exec yarn serve

@bryanlarsen right, a separate wrapper is not needed using nvm-exec

@bryanlarsen Great tip. Note that if you don't want to hard-code the node version into the service file at all, you can keep an up-to-date .nvmrc file in your project folder. No need to define NODE_VERSION in the environment:

ExecStart=/opt/nvm/nvm-exec npm start

For anyone else landing here from google, here is a full systemd service definition:

[Unit]
Description=example.com
After=network.target

[Service]
Environment=NODE_VERSION=6.9.1
Type=simple
User=linus
ExecStart=/home/linus/.nvm/nvm-exec node .
Restart=on-failure
WorkingDirectory=/home/linus/example.com

[Install]
WantedBy=multi-user.target

You want to replace:

  • User=linus with the user you want running the service
  • ExecStart=/home/linus/.nvm/nvm-exec with the path to your installation of Nvm
  • WorkingDirectory=/home/linus/example.com with the directory where your Node.js app is installed
Was this page helpful?
0 / 5 - 0 ratings