123
votes

I am in the process of building a new Docker image and I'm looking to get NVM installed so I can manage nodejs.

Reading the docs on how to install NVM they mention that you need to source your .bashrc file in order to start using NVM.

I've tried to set this up in a Dockerfile, but so far building fails with the error:

"bash: nvm: command not found"

Here are the relevant lines from my Dockerfile:

ADD files/nvm_install.sh /root/
RUN chmod a+x  /root/nvm_install.sh
RUN bash -c "/root/nvm_install.sh"
RUN bash -l -c "source /root/.bashrc"
RUN cd /root
RUN bash -l -c "nvm install 0.10.31"

Here is the output from trying to build:

docker build -t nginx_dock .

Step 0 : FROM ubuntu
---> 826544226fdc
Step 1 : MAINTAINER dficociello
---> Using cache
---> da3bc340fbb3
Step 2 : RUN apt-get update
---> Using cache
---> 6b6b611feb4f
Step 3 : RUN apt-get install nginx curl -y
---> Using cache
---> 159eb0b16d23
Step 4 : RUN touch /root/.bashrc
---> Using cache
---> 5e9e8216191b
Step 5 : ADD files/nginx.conf /etc/nginx/
---> Using cache
---> c4a4a11296a2
Step 6 : ADD files/nvm_install.sh /root/
---> Using cache
---> b37cba2a18ca
Step 7 : RUN chmod a+x  /root/nvm_install.sh
---> Using cache
---> bb13e2a2893d
Step 8 : RUN bash -c "/root/nvm_install.sh"
---> Using cache
---> 149b49a8fc71
Step 9 : RUN bash -l -c "source /root/.bashrc"
---> Running in 75f353ed0d53
---> 0eae8eae7874
Removing intermediate container 75f353ed0d53
Step 10 : RUN cd /root
---> Running in feacbd998dd0
---> 284293ef46b0
Removing intermediate container feacbd998dd0
Step 11 : RUN bash -l -c "nvm install 0.10.31"
---> Running in 388514d11067
bash: nvm: command not found
2014/09/17 13:15:11 The command [/bin/sh -c bash -l -c "nvm install 0.10.31"] returned a non-zero         code: 127

I'm pretty new to Docker so I may be missing something fundamental to writing Dockerfiles, but so far all the reading I've done hasn't shown me a good solution.

16
Not a duplicate as this is particular to installing inside docker and not a standard install - David Ficociello
Since you're only installing a single version of node, it seems unnecessary to install nvm. The Dockerfiles provided by the Docker node repository can be used as a reference for how to install node. - mikwat

16 Answers

137
votes

When you RUN bash... each time that runs in a separate process, anything set in the environment is not maintained. Here's how I install nvm:

# Replace shell with bash so we can source files
RUN rm /bin/sh && ln -s /bin/bash /bin/sh

# Set debconf to run non-interactively
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections

# Install base dependencies
RUN apt-get update && apt-get install -y -q --no-install-recommends \
        apt-transport-https \
        build-essential \
        ca-certificates \
        curl \
        git \
        libssl-dev \
        wget \
    && rm -rf /var/lib/apt/lists/*

ENV NVM_DIR /usr/local/nvm # or ~/.nvm , depending
ENV NODE_VERSION 0.10.33

# Install nvm with node and npm
RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.20.0/install.sh | bash \
    && . $NVM_DIR/nvm.sh \
    && nvm install $NODE_VERSION \
    && nvm alias default $NODE_VERSION \
    && nvm use default

ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules
ENV PATH      $NVM_DIR/v$NODE_VERSION/bin:$PATH
35
votes

To help everyone that are looking for a way to install the Node.js with NVM on Ubuntu (last version), I made the dockerfile below. I'm using the last version of Docker, Ubuntu, Node.js and the NVM is working properly (the $PATH was fixed). I'm using this in a production environment.

$ docker info \
Server Version: 1.9.1
Kernel Version: 4.1.13-boot2docker
Operating System: Boot2Docker 1.9.1 (TCL 6.4.1); master : cef800b - Fri Nov 20 19:33:59 UTC 2015

Node.js Version: stable 4.2.4 LTS
Ubuntu Version: 14.04.3


dockerfile:

FROM ubuntu:14.04.3

# Replace shell with bash so we can source files
RUN rm /bin/sh && ln -s /bin/bash /bin/sh

# make sure apt is up to date
RUN apt-get update --fix-missing
RUN apt-get install -y curl
RUN apt-get install -y build-essential libssl-dev

ENV NVM_DIR /usr/local/nvm
ENV NODE_VERSION 4.2.4

# Install nvm with node and npm
RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.30.1/install.sh | bash \
    && source $NVM_DIR/nvm.sh \
    && nvm install $NODE_VERSION \
    && nvm alias default $NODE_VERSION \
    && nvm use default

ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules
ENV PATH      $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH

RUN mkdir /usr/app
RUN mkdir /usr/app/log

WORKDIR /usr/app

# log dir
VOLUME /usr/app/log

# Bundle app source
COPY . /usr/app
# Install app dependencies
RUN npm install

EXPOSE  3000
CMD ["node", "server.js"]
28
votes

Update 20/02/2020: This solution works if you're using a debian base image. If you're using ubuntu, see this answer.

Here is the cleanest way to install nvm that I have found:

SHELL ["/bin/bash", "--login", "-c"]

RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
RUN nvm install 10.15.3

Explanation

  • The first line sets the Dockerfile's default shell to a bash login shell. Note: this means that every subsequent RUN, CMD, and ENTRYPOINT will be run under the current user (usually root), and source the ~/.bashrc file if run in the shell form.

  • The second line installs nvm with bash. When the script is run with bash, it appends to the ~/.bashrc file.

  • The third line installs a particular version of nodejs and uses it. The nvm, npm, and node commands are available because they are run via a bash login shell (see line 1).

27
votes

Nvm paths have changed since the accepted answer, so if you want to use a more up-to-date nvm version, you need to make a few changes. Also, it is not necessary to remap sh to make it work:

ENV NVM_DIR /usr/local/nvm
RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh | bash
ENV NODE_VERSION v7.9.0
RUN /bin/bash -c "source $NVM_DIR/nvm.sh && nvm install $NODE_VERSION && nvm use --delete-prefix $NODE_VERSION"

ENV NODE_PATH $NVM_DIR/versions/node/$NODE_VERSION/lib/node_modules
ENV PATH      $NVM_DIR/versions/node/$NODE_VERSION/bin:$PATH

Not sure if you will need the --delete-prefix option on the nvm use - I did, but that may be something strange about my base image.

20
votes

Each RUN in a Dockerfile is executed in a different container. So if you source a file in a container, its content will not be available in the next one.

That is why when you install an application and you need to do several steps, you must do it in the same container.

With your example:

ADD files/nvm_install.sh /root/
RUN chmod a+x /root/nvm_install.sh && \
  /root/nvm_install.sh && \
  source /root/.bashrc && \
  cd /root && \
  nvm install 0.10.31
17
votes

This is based on the top answer and works in 2018:

# Replace shell with bash so we can source files
RUN rm /bin/sh && ln -s /bin/bash /bin/sh

# Install base dependencies
RUN apt-get update && apt-get install -y -q --no-install-recommends \
        apt-transport-https \
        build-essential \
        ca-certificates \
        curl \
        git \
        libssl-dev \
        wget

ENV NVM_DIR /usr/local/nvm
ENV NODE_VERSION 8.11.3

WORKDIR $NVM_DIR

RUN curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash \
    && . $NVM_DIR/nvm.sh \
    && nvm install $NODE_VERSION \
    && nvm alias default $NODE_VERSION \
    && nvm use default

ENV NODE_PATH $NVM_DIR/versions/node/v$NODE_VERSION/lib/node_modules
ENV PATH      $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH

Note that nvm is not a bash command, it is an alias. This can screw you up if you're relying on $PATH.

17
votes

Took me an hour or two to figure out the cleanest way to do it. --login doesn't seem to execute .bashrc so you have to supply -i to launch it in interactive mode. This causes Docker to yell at you for a bit so I only launch this way for the installation, then reset to my standard shell.

# Installing Node
SHELL ["/bin/bash", "--login", "-i", "-c"]
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash
RUN source /root/.bashrc && nvm install 12.14.1
SHELL ["/bin/bash", "--login", "-c"]
11
votes

Here is my working version

FROM ubuntu:14.04

# Declare constants
ENV NVM_VERSION v0.29.0
ENV NODE_VERSION v5.0.0

# Replace shell with bash so we can source files
RUN rm /bin/sh && ln -s /bin/bash /bin/sh

# Install pre-reqs
RUN apt-get update
RUN apt-get -y install curl build-essential

# Install NVM
RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/${NVM_VERSION}/install.sh | bash

# Install NODE
RUN source ~/.nvm/nvm.sh; \
    nvm install $NODE_VERSION; \
    nvm use --delete-prefix $NODE_VERSION;

Took help from @abdulljibali and @shamisis answers.

10
votes

Based upon the suggestion in @Kuhess answer, I replaced the source command with the following in my Dockerfile

RUN cat ~/.nvm/nvm.sh >> installnode.sh
RUN echo "nvm install 0.10.35" >> installnode.sh
RUN sh installnode.sh
8
votes

A key difference between the attempt to get the nvm command in the question:

RUN bash -l -c "source /root/.bashrc"

which doesn't work and the attempt to do the same in the accepted answer:

source $NVM_DIR/nvm.sh

Is that the second version sources the nvm.sh script directly, whereas the original tries to do it via the .bashrc file.

The .bashrc file has a line in it early on which exits if it's being run in a non interactive shell:

# If not running interactively, don't do anything
case $- in
    *i*) ;;
    *) return;;
esac

So it never gets to the bit where it would have sourced nvm.sh which actually puts the nvm command in your shell.

I wouldn't be surprised if docker is running this stuff in a non interactive shell. This hadn't been explicitly pointed out, so I thought I would mention it as it's what caught me out when I was doing something similar with vagrant.

7
votes

I must begin with the fact that I searched all over to get a working example of nvm inside docker and I found none. Even the answers in this thread did not work.

So, I spent quite some time and came up with one that works:

# install dependencies
RUN apt-get update && apt-get install -y \
      curl \
      npm \
      nodejs \
      git;

# compatibility fix for node on ubuntu
RUN ln -s /usr/bin/nodejs /usr/bin/node;

# install nvm
RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.24.1/install.sh | sh;

# invoke nvm to install node
RUN cp -f ~/.nvm/nvm.sh ~/.nvm/nvm-tmp.sh; \
    echo "nvm install 0.12.2; nvm alias default 0.12.2" >> ~/.nvm/nvm-tmp.sh; \
    sh ~/.nvm/nvm-tmp.sh; \
    rm ~/.nvm/nvm-tmp.sh;

Notice how I have installed nodejs via apt-get as well. I found that some packages don't get installed inside docker unless this is done.

6
votes

Just one answer put the curl installation but does not work the entire Dockerfile

Here my Dockerfile ready to copy/paste in which I install latest nvm 2020 version with Ubuntu 18.04.3 LTS

FROM ubuntu

RUN apt-get update
RUN echo "y" | apt-get install curl
ENV NVM_DIR /usr/local/nvm
RUN mkdir -p /usr/local/nvm
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
ENV NODE_VERSION v10
RUN /bin/bash -c "source $NVM_DIR/nvm.sh && nvm install $NODE_VERSION && nvm use --delete-prefix $NODE_VERSION"

ENV NODE_PATH $NVM_DIR/versions/node/$NODE_VERSION/lib/node_modules
ENV PATH      $NVM_DIR/versions/node/$NODE_VERSION/bin:$PATH
4
votes

None of these worked for me, for my python3-onbuild container I had to force-create symbolic links to the nvm installation.

# Install npm and nodejs
RUN apt-get install -y build-essential libssl-dev

RUN mkdir /root/.nvm
ENV NVM_DIR /root/.nvm
ENV NODE_VERSION 8.9.4

RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.9/install.sh | bash
RUN chmod +x $HOME/.nvm/nvm.sh
RUN . $HOME/.nvm/nvm.sh && nvm install $NODE_VERSION && nvm alias default $NODE_VERSION && nvm use default && npm install -g npm

RUN ln -sf /root/.nvm/versions/node/v$NODE_VERSION/bin/node /usr/bin/nodejs
RUN ln -sf /root/.nvm/versions/node/v$NODE_VERSION/bin/node /usr/bin/node
RUN ln -sf /root/.nvm/versions/node/v$NODE_VERSION/bin/npm /usr/bin/npm
2
votes

25-Feb-2021 The main problem is with use of the 'source' directive, which is bash shell specific. What worked for me was replacing 'source' with '.' for a Ubuntu 18 install. My Dockerfile:

FROM ubuntu:bionic

RUN \
  apt update && \
  apt upgrade -y && \
  apt install -y curl

ENV NVM_DIR /root/.nvm
ENV NODE_VERSION 14.16

# Install nvm with node and npm
RUN curl -sL https://raw.githubusercontent.com/creationix/nvm/v0.35.3/install.sh | bash \
  && . $NVM_DIR/nvm.sh \ 
  && nvm install $NODE_VERSION
1
votes

This is what worked for me (I'm using debian buster):

RUN apt-get update
RUN apt-get install -y build-essential checkinstall libssl-dev
RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.35.1/install.sh | bash
SHELL ["/bin/bash", "--login", "-c"]

You should now be able to do nvm install <version>.

1
votes

This installs the lts-version of nodejs when extending image "php:7.4.15" (debian:buster-slim):

# Install nvm to install npm and node.js
ENV NVM_DIR /root/.nvm
ENV NODE_VERSION lts/*
RUN mkdir $HOME/.nvm && \
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash && \
    chmod +x $HOME/.nvm/nvm.sh && \
    . $HOME/.nvm/nvm.sh && \
    nvm install --latest-npm "$NODE_VERSION" && \
    nvm alias default "$NODE_VERSION" && \
    nvm use default && \
    DEFAULT_NODE_VERSION=$(nvm version default) && \
    ln -sf /root/.nvm/versions/node/$DEFAULT_NODE_VERSION/bin/node /usr/bin/nodejs && \
    ln -sf /root/.nvm/versions/node/$DEFAULT_NODE_VERSION/bin/node /usr/bin/node && \
    ln -sf /root/.nvm/versions/node/$DEFAULT_NODE_VERSION/bin/npm /usr/bin/npm