Mind~G
Docker

Optimize your Docker Image

Optimize your Dockerfile

  • Use a multi-stage build to reduce the size of the final image.
  • Use a smaller base image.
  • In multi-stage builds, we only copy that code that we need to run the application.

Scenario

We have a Dockerfile for a project written using Express, TypeScript, and Node.js.

As we know, when we build the project, it creates a dist folder. Inside this folder, we get the index.js file, which is generated by the TypeScript compiler. We can run this file to start the app.

During development, we just run npm run dev without building the code. This works fine because it uses tools like ts-node to run TypeScript directly.

But in production, we always build the code first. This creates the dist folder, and then we run the app from there.

If we create a Dockerfile that copies the whole codebase and builds it inside the image, the image becomes large in size. But we don’t actually need to copy everything. We only need to copy the final dist folder and run the app from it.

To reduce the image size, we can use a multi-stage build in Docker. This allows us to build the code in one stage and then only copy the needed files (like dist, package.json, etc.) into the final image.

Not Optimized Dockerfile:

FROM node:20-alpine
WORKDIR /home/app
COPY package.json* .
RUN npm install
COPY . .
EXPOSE 3001
CMD ["npm", "start"]

Optimized Dockerfile(Multi-stage Build)

FROM node:20-alpine as builder
# Stage 1: Build
WORKDIR /home/build
COPY package*.json .
COPY tsconfig.json .
RUN npm install
COPY . .
RUN npm run build
# Stage 2: Runner
FROM builder as runner
WORKDIR /home/app
COPY --from=builder /home/build/dist ./dist
COPY --from=builder /home/build/package*.json  .
RUN npm install --omit=dev
EXPOSE 3001
CMD ["npm", "start"]

Note:

  • as builder is used to create a builder stage.
  • as runner is used to create a runner stage.
  • COPY --from=builder /home/build/dist ./dist is used to copy the dist folder from the builder stage to the runner stage.
  • COPY --from=builder /home/build/package*.json . is used to copy the package.json and package-lock.json files from the builder stage to the runner stage.
  • RUN npm install --omit=dev is used to install the dependencies in the runner stage because we don't need the dev dependencies in the runner stage.
  • EXPOSE 3001 is used to expose the port of the app.

On this page