Today, I’ll walk you through how to dockerize a NestJS application. This will help you package your app into a container that can run consistently across different environments. Let’s get started!
Step 1: Setting Up the Dockerfile
First, we need to create a Dockerfile
in the root directory of your NestJS project. This file will contain instructions for building your Docker image.
Here’s what the basic Dockerfile
might look like:
# Use Node.js version 20 as the base image
FROM node:20
# Set the working directory in the container
WORKDIR /usr/src/app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy the rest of the application code
COPY . .
# Build the application
RUN npm run build
# Run the application
CMD ["node", "dist/main.js"]
This Dockerfile does the following:
- Uses Node.js version 20 as the base image.
- Sets the working directory to
/usr/src/app
. - Copies
package.json
andpackage-lock.json
to the working directory. - Installs the dependencies using
npm install
. - Copies the rest of the application code into the container.
- Builds the application using
npm run build
. - Specifies the command to run the application with
node dist/main.js
.
Create a .dockerignore
file to exclude unnecessary files from the Docker image.
node_modules
npm-debug.log
dist
.git
.env
*.md
.gitignore
Step 2: Testing the Docker Container Locally
Now, let’s test our Docker image locally to make sure everything works as expected.
Build the Docker Image:
Open your terminal at the root of your project and run:
docker build -t nest-app .
This command builds the Docker image and tags it with nest-app
.
Run the Docker Container:
After building the image, you can run it with:
docker run -p 3000:3000 nest-app
This command starts a new container from the nest-app
image and maps port 3000 of the container to port 3000 on your local machine.
Verify the Application:
Open your web browser and go to http://localhost:3000
. If everything is set up correctly, you should see your NestJS application running.
Step 3: Optimizing for Production
To optimize the Dockerfile for production, we’ll make a few changes to reduce the image size and improve security.
Use Alpine Node Images:
Alpine images are smaller and more efficient. Update the base image to:
FROM node:20-alpine
Set NODE_ENV Environment Variable:
Set NODE_ENV
to production
to optimize the application for production:
ENV NODE_ENV production
Use npm ci
Instead of npm install
:
npm ci
is more reliable for automated environments like Docker builds. Replace RUN npm install
with:
RUN npm ci
Add USER Instruction:
Run the application as a non-root user for better security. Add this after installing dependencies:
USER node
Use Multistage Builds:
Multistage builds allow you to create a smaller final image by separating the build and runtime environments. Here’s an example of a multistage Dockerfile:
# Development stage
FROM node:20-alpine AS development
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm ci
COPY . .
USER node
# Build stage
FROM node:20-alpine AS build
WORKDIR /usr/src/app
COPY package*.json ./
COPY --from=development /usr/src/app/node_modules ./node_modules
COPY . .
RUN npm run build
ENV NODE_ENV production
RUN npm ci --only=production && npm cache clean --force
USER node
# Production stage
FROM node:20-alpine AS production
WORKDIR /usr/src/app
COPY --from=build /usr/src/app/node_modules ./node_modules
COPY --from=build /usr/src/app/dist ./dist
CMD ["node", "dist/main.js"]
This setup uses three stages:
-
development
: For local development with all dependencies. -
build
: Builds the application for production. -
production
: The final stage that runs the production build.
Step 4: Rebuilding and Running the Optimized Image
After updating your Dockerfile
, rebuild the image:
docker build -t nest-app .
Then, run the new optimized container:
docker run -p 3000:3000 nest-app
Check the size of the new image with:
docker images
You should see a significant reduction in the image size, making it more efficient for production use.
Troubleshooting Common Issues
-
Error: Cannot find module ‘webpack’: Ensure you’re using the correct Node.js version in your base image. For example, use
FROM node:20-alpine
instead ofFROM node:14-alpine
. -
Error: nest command not found: This usually happens if the Nest CLI is not installed. In a multistage build, make sure you copy over the
node_modules
directory from the development stage where the CLI is installed.
Using Different Package Managers
If you’re using pnpm instead of npm, your Dockerfile
would look slightly different. Here’s an example:
# Development stage
FROM node:20 AS development
RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm
WORKDIR /usr/src/app
COPY pnpm-lock.yaml ./
RUN pnpm fetch --prod
COPY . .
RUN pnpm install
USER node
# Build stage
FROM node:20 AS build
RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm
WORKDIR /usr/src/app
COPY pnpm-lock.yaml ./
COPY --from=development /usr/src/app/node_modules ./node_modules
COPY . .
RUN pnpm build
ENV NODE_ENV production
RUN pnpm install --prod
USER node
# Production stage
FROM node:20-alpine AS production
WORKDIR /usr/src/app
COPY --from=build /usr/src/app/node_modules ./node_modules
COPY --from=build /usr/src/app/dist ./dist
CMD ["node", "dist/main.js"]
Using Fastify with NestJS
If you’re using Fastify instead of Express, you’ll need to modify your main.ts
to listen on 0.0.0.0
:
import { NestFactory } from '@nestjs/core';
import {
FastifyAdapter,
NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter(),
);
await app.listen(process.env.PORT || 3000, '0.0.0.0');
}
bootstrap();
That’s it! You now have a dockerized NestJS application that’s optimized for production. If you want to deploy your NestJS app, check out sliplane.io
Cheers,
Jonas
Source link
lol