Docker images can get very large if you use an official Node image to run on your server. But you don't need most of the features it offers to build a small project. I will show how to reduce the size of an image from 1.36 GB to 140 MB using a multi-stage build. First, let's build a starter image of my Node.js project:

FROM node:16
WORKDIR /project
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
CMD node build/index.js

initial image size There are quite a few problems with this:

  • We included node_modules and git folders in our build.
  • We used all dev dependencies that are redundant in production.

Let's solve these issues. Add the .gitignore file:

.git
build
node_modules

And remove development dependencies after the build command:

RUN npm prune --omit=dev

Shaving off 100 MB as a result of such a simple operation is pretty good, but we can do much more with a multi-stage build and node-alpine image. The first step is to tag our initial build as "initial":

FROM node:16 as initial

Now, we add a new stage with node-alpine image and copy compiled build files and node_modules from the initial build:

FROM node:16-alpine
WORKDIR /project

COPY --from=initial /project/node_modules /project/node_modules
COPY --from=initial /project/build /project

The final version of the Dockerfile will look like this:

# stage 1
FROM node:16 as initial
WORKDIR /project
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
RUN npm prune --omit=dev

# stage 2
FROM node:16-alpine
WORKDIR /project

COPY --from=initial /project/node_modules /project/node_modules
COPY --from=initial /project/build /project
CMD node index.js

Now, let's see the result of this build: final refactor This final image works just as well as the first one, and we shaved off ~1.2 GB.