This article provide a straightforward guide to dockerizing NestJS application for both develpoment and production environments.
Dockerfile for Development
First, let’s create a Dockerfile.dev
for the development environment within our NestJS repository:
# Dockerfile.dev
FROM node:20-alpine
WORKDIR /usr/src/app
EXPOSE 3000
CMD ["npm", "run", "start:dev"]
COPY package*.json ./
RUN npm install
COPY . .
In this Dockerfile, we specify the Node.js version along with the Alpine Linux distribution to minimize the image size. Note the order of commands such as EXPOSE
and CMD
to optimize Docker’s caching mechanism. Since code modifications are frequent during development, we place the COPY
command towards the end of the Dockerfile to reduce rebuild time.
Now, let’s build the image from Dockerfile.dev
:
$ docker build -t nest-dev -f Dockerfile.dev .
After building, let’s check the image size:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nest-dev latest 7105d40d77e3 About a minute ago 327MB
Building this Docker image with the base image node:20-alpine
yields a size of 327MB, which is quite efficient. However, if we were to use node:20
instead of node:20-alpine
as the base image, the image size would increase significantly, up to 1.29GB.
Dockerfile for Production
Even though 327MB is relatively small, we can further optimize the image size for the production environment by removing unnecessary files and packages during the build process. Here’s the Dockerfile.prod
for production:
FROM node:20-alpine AS builder
COPY . .
RUN npm install && npm run build && npm install --prod
FROM node:20-alpine
WORKDIR /usr/src/app
COPY --from=builder /node_modules ./node_modules
COPY --from=builder /dist ./dist
COPY /.env ./.env
EXPOSE 3000
CMD ["node", "./dist/main"]
In this Dockerfile, we utilize Multi-stage builds to first compile the TypeScript code into JavaScript and then install only the production dependencies, thereby significantly reducing the image size.
Let’s build the production image and compare the size difference with the development version:
$ docker build -t nest-prod -f Dockerfile.prod .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nest-dev latest 7105d40d77e3 About a minute ago 327MB
nest-prod latest 04f3894b4cf6 About a minute ago 141MB
As you can see, the production image size is half of the development version, resulting in reduced tranportation time and storage requirements.