1. Introduction
Applications with non-standard dependencies can have us using a custom Node.js image instead of an official image. Luckily, there are many ways to create a custom Node.js from a Dockerfile.
In this tutorial, we’ll discuss how to install Node.js in a Dockerfile.
2. Using fnm
Using fnm, we can create a Dockerfile that gives rise to a Node.js image. But first, what is fnm? fnm (short for Fast Node Manager) is a tool for installing and managing Node.js.
We’ll start with ubuntu:24.04 (a random choice) as our base image:
FROM ubuntu:24.04
Next, we’ll specify our preferred Node.js version using an ARG instruction:
ARG version=20
Then, we’ll add a RUN instruction to install curl, unzip, and fnm. We need curl to download the fnm ZIP package and unzip to extract files from the package. After that, we’ll add a command that copies the fnm binary to a PATH directory and an fnm install command to install Node.js:
RUN apt update -y && apt install curl unzip -y \
&& curl -fsSL https://fnm.vercel.app/install | bash -s -- --install-dir './fnm' \
&& cp ./fnm/fnm /usr/bin && fnm install $version
Finally, we’ll use tail -f /dev/null as our ENTRYPOINT to keep the ensuing container running:
ENTRYPOINT tail -f /dev/null
Our final Dockerfile will look like this:
$ cat Dockerfile
FROM ubuntu:24.04
ARG version=20
RUN apt update -y && apt install curl unzip -y \
&& curl -fsSL https://fnm.vercel.app/install | bash -s -- --install-dir './fnm' \
&& cp ./fnm/fnm /usr/bin && fnm install $version
ENTRYPOINT tail -f /dev/null
To confirm the installation, we’ll build the image, create a container, and get into the container’s shell. To do this, we’ll combine docker run, docker build, and docker exec commands:
$ docker exec -it $(docker run --rm -d $(docker build -q .)) bash
In the command above, docker build -q . returns only the image ID. Then, docker run uses that image ID to create the container. Adding –rm to docker run ensures the container is automatically removed on exit, while -d makes the container detached. On execution, docker run returns the container ID, then docker exec uses that ID to create and enter the container’s shell.
Once we’re in the container’s shell, we’ll run node -v:
# node -v
v20.15.0
As specified, the installed Node.js is version 20.15.0.
3. Installing From Binary File
Installing Node.js in a Dockerfile from a prebuilt binary file is pretty straightforward. We only have to configure the Dockerfile to download the compressed binary package, extract the files and directories, and add the binaries to PATH.
As before, we’ll use ubuntu:24.04 as our base image. We’ll also declare an ARG variable for our desired Node.js version:
FROM ubuntu:24.04
ARG version=v20.15.0
The Node.js official download page provides a list of available prebuilt binary versions.
Next, we’ll install curl and download the prebuilt Node.js binary archive file:
RUN apt update -y && apt install curl -y \
&& curl -fsSL https://nodejs.org/dist/$version/node-$version-linux-x64.tar.gz -o node.tar.gz \
Then, we’ll extract files from the archive and add the binaries to PATH:
&& tar -xzvf node.tar.gz && rm node.tar.gz \
&& echo "export PATH=$PATH:/node-$version-linux-x64/bin" >> /root/.bashrc
Finally, we’ll use sleep infinity as our ENTRYPOINT, so the container keeps running:
ENTRYPOINT sleep infinity
Now, we have our Dockerfile:
$ cat Dockerfile
FROM ubuntu:24.04
ARG version=v20.15.0
RUN apt update -y && apt install curl -y \
&& curl -fsSL https://nodejs.org/dist/$version/node-$version-linux-x64.tar.gz -o node.tar.gz \
&& tar -xzvf node.tar.gz && rm node.tar.gz \
&& echo "export PATH=$PATH:/node-$version-linux-x64/bin" >> /root/.bashrc
ENTRYPOINT sleep infinity
To confirm our installation, we’ll build the image, create a container from it, and start a bash shell as the main process:
$ docker exec -it $(docker run --rm -d $(docker build -q .)) bash
When in the container’s shell, we’ll run node -v:
# node -v
v20.15.0
We see the expected version number in the output.
4. Installing From a Package Manager
We can install Node.js using our base image’s package manager. However, this method limits our control over the version of Node.js we install.
When using a Debian or Ubuntu base image, we can install Node.js in our Dockerfile with apt:
$ cat Dockerfile
FROM ubuntu:24.04
RUN apt update -y && apt install nodejs -y
ENTRYPOINT node -v
In this illustration, we used node -v as our ENTRYPOINT, so we can readily confirm the installation:
$ docker run --rm $(docker build -q .)
v18.19.1
If our base image was CentOS, Fedora, or RHEL, we’ll use dnf:
$ cat Dockerfile
FROM fedora
RUN dnf install nodejs -y
ENTRYPOINT node -v
Then, we’ll confirm the installation:
$ docker run --rm $(docker build -q .)
v20.12.2
For an Alpine base image, we’ll use apk:
$ cat Dockerfile
FROM alpine
RUN apk add nodejs npm
ENTRYPOINT node -v
Once again, we’ll confirm the installation:
$ docker run --rm $(docker build -q .)
v20.13.1
As we can see from the output, the version that gets installed will depend on the distribution we’re using.
5. Installing From Source Code
Installing from source code takes a lot of time and is memory-intensive. However, it allows us to install any version of Node.js.
To install Node.js in the Dockerfile from source code, we’ll define a base image and version variable:
FROM ubuntu:24.04
ARG version=v22.3.0
Next, we’ll add a command that installs the dependencies:
RUN apt update -y && apt install curl make g++ python3 python3-pip -y
After that comes the command that downloads and extracts the source code:
RUN apt update -y && apt install curl make g++ python3 python3-pip -y \
&& curl -fsSL https://nodejs.org/dist/$version/node-$version.tar.gz -o node.tar.gz \
&& tar -xzvf node.tar.gz && rm node.tar.gz
Next, we change the working directory, execute the configure script, and run make commands to install Node.js:
WORKDIR node-$version
RUN ./configure && make && make install node
ENTRYPOINT node -v
This leaves us with our final Dockerfile:
$ cat Dockerfile
FROM ubuntu:24.04
ARG version=v20.15.0
RUN apt update -y && apt install curl make g++ python3 python3-pip -y \
&& curl -fsSL https://nodejs.org/dist/$version/node-$version.tar.gz -o node.tar.gz \
&& tar -xzvf node.tar.gz && rm node.tar.gz
WORKDIR node-$version
RUN ./configure && make && make install node
ENTRYPOINT node -v
Finally, we confirm the installation:
$ docker run --rm $(docker build -q .)
v22.3.0
We’ve successfully managed to build Node.js from source.
6. Conclusion
In this article, we showed how to install Node.js in a Dockerfile using fnm, a prebuilt binary, Linux package managers, and source code. Of all four options, the source code method is the slowest, but most flexible. On the other hand, the fnm and prebuilt binary methods offer a balance of flexibility and ease of use.