Table of contents
What is Node.js?
Node.js is a software platform for scalable server-side and networking applications. Node.js applications are written in JavaScript and can be run within the Node.js runtime on Mac OS X, Windows, and Linux without changes.
Node.js applications are designed to maximize throughput and efficiency, using non-blocking I/O and asynchronous events. Node.js applications run single-threaded, although Node.js uses multiple threads for file and network events. Node.js is commonly used for real-time applications due to its asynchronous nature.
Node.js internally uses the Google V8 JavaScript engine to execute code; a large percentage of the basic modules are written in JavaScript. Node.js contains a built-in, asynchronous I/O library for file, socket, and HTTP communication. The HTTP and socket support allows Node.js to act as a web server without additional software such as Apache.
Creating a Dockerfile
Create an empty file called Dockerfile
:
touch Dockerfile
it's a good idea to build docker production image from a stable long term support version of nodejs . that's why i am using nodejs 16 LTS docker image.
Now Write Simple Docker Container
standard size of a node js image 900+ MB without compression and minimization.
# we will take the long term release version of docker
FROM node:16
# we want to be a production image
ENV NODE_ENV=production
# this is the directory we will post the app
WORKDIR /app
# copy the package.json and lockfile
COPY ["package.json","package-lock.json","./"]
# perform a clean npm install to install dependency
RUN npm ci --production
# set the user as node, so we dont run as root
USER node
# set the user as node,so we dont run as root
COPY src/. .
# start the server
CMD ["node","server.js"]
Nodejs container with nodejs alpine image
alpine image is comparatively smaller than standard nodejs image.
FROM node:16-alpine3.15
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --production
ENV PORT 5050
EXPOSE 5050
CMD ["node","build"]
Multi stage nodejs slim docker container build
docker-slim
will optimize and secure your containers by understanding your application and what it needs using various analysis techniques. It will throw away what you don't need, reducing the attack surface of your container. What if you need some of those extra things to debug your container? You can use dedicated debugging side-car containers for that (more details below).
we decided to use slim
(~100 MB),
# we will take the long term release version of docker
FROM node:16-alpine3.15 as builder
# we want to be a production image
ENV NODE_ENV=production
# this is the directory we will post the app
WORKDIR /app
# copy the package.json and lockfile
COPY ["package.json","package-lock.json","./"]
# perform an npm install to install dependency
RUN npm ci --production
# set the user as node, so we dont run as root
USER 0
# set the user as node,so we dont run as root
COPY src/. .
FROM node:16.17-slim
USER node
COPY --from=builder /app /app
WORKDIR /app
# start the server
CMD ["node","server.js"]
Distroless node js docker Container
"Distroless" images contain only your application and its runtime dependencies. They do not contain package managers, shells or any other programs you would expect to find in a standard Linux distribution.
Distroless images are very small. The smallest distroless image, gcr.io/distroless/static-debian11
, is around 2 MiB. That's about 50% of the size of alpine
(~5 MiB), and less than 2% of the size of debian
(124 MiB).
# we will take the long term release version of docker
FROM node:16-alpine3.15 as builder
# we want to be a production image
ENV NODE_ENV=production
# this is the directory we will post the app
WORKDIR /app
# copy the package.json and lockfile
COPY ["package.json","package-lock.json","./"]
# perform an npm install to install dependency
RUN npm ci --production
# set the user as node, so we dont run as root
USER 0
# set the user as node,so we dont run as root
COPY src/. .
FROM gcr.io/distroless/nodejs:14
USER node
COPY --from=builder /app /app
WORKDIR /app
# start the server
CMD ["node","server.js"]
Red Hat Universal Base nodejs docker image
Red Hat Universal Base Images (UBI) are OCI-compliant container base operating system images with complementary runtime languages and packages that are freely redistributable.
Node.js 16 available as container is a base platform for building and running various Node.js 16 applications and frameworks. Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.
# we will take the long term release version of docker
FROM registry.access.redhat.com/ubi8/nodejs-14 as builder
# we want to be a production image
ENV NODE_ENV=production
# this is the directory we will post the app
WORKDIR /app
# copy the package.json and lockfile
COPY ["package.json","package-lock.json","./"]
# perform an npm install to install dependency
RUN npm ci --production
# set the user as node, so we dont run as root
USER 100
# set the user as node,so we dont run as root
COPY src/. .
FROM quay.io/chrishayuk/nodejs-base:14.15.4-ubi-minimal
USER node
COPY --from=builder /app /app
WORKDIR /app
# start the server
CMD ["node","server.js"]
Distroless nodejs docker image
"Distroless" images contain only your application and its runtime dependencies. They do not contain package managers, shells or any other programs you would expect to find in a standard Linux distribution.
Why should I use distroless images?
Restricting what's in your runtime container to precisely what's necessary for your app is a best practice employed by Google and other tech giants that have used containers in production for many years. It improves the signal to noise of scanners (e.g. CVE) and reduces the burden of establishing provenance to just what you need.
Distroless images are very small. The smallest distroless image, gcr.io/distroless/static-debian11
, is around 2 MiB. That's about 50% of the size of alpine
(~5 MiB), and less than 2% of the size of debian
(124 MiB).
FROM node:16-alpine3.15 AS build-env
COPY . /app
WORKDIR /app
RUN npm ci --omit=dev
FROM gcr.io/distroless/nodejs:18
COPY --from=build-env /app /app
WORKDIR /app
CMD ["index.js"]
For node js docker image
alpine image
NodeJs offical image
debian image
debian slim image
its recomended to use the same type of distro for multi stage build , ie when building with nodejs offical image use debian/debian slim ,
but for smallest image size use alpine image
You can find more information about Docker and Node.js on Docker in the following places: