Skip to main content

Deploying NestJS with Docker

A Dockerfile that containerizes a NestJS application with a Docker multi-stage build. Separating the build and runtime stages minimizes image size.

Multi-stage build strategy

Build stage (builder):

  • Based on the Node 16 Alpine image
  • Upgrade npm/yarn to their latest versions
  • Install dependencies and compile TypeScript
  • Separate privileges with the node user

Runtime stage:

  • Copy only the build output (dist/) to reduce image size
  • Exclude unnecessary devDependencies
  • Install native libraries required in the Alpine environment

Dockerfile

FROM node:16-alpine as builder

RUN npm install -g --force npm@latest yarn@latest

ENV NODE_ENV build
RUN mkdir /app && chown -R node:node /app
WORKDIR /app

COPY package.json yarn.lock nest-cli.json tsconfig.* bin/ src/ /app

RUN chown -R node:node /app

USER node
RUN yarn install --frozen-lockfile --force \
&& yarn build

# ---

FROM node:16-alpine

ENV NODE_ENV production
# ENV TNS_ADMIN /app/secrets/
# ENV LD_LIBRARY_PATH=/lib

RUN sed -i 's/http\:\/\/dl-cdn.alpinelinux.org/https\:\/\/alpine.global.ssl.fastly.net/g' /etc/apk/repositories
RUN apk --no-cache add libaio libnsl libc6-compat curl

USER node
WORKDIR /app
ENV NODE_ENV production

COPY --from=builder /app/package*.json /app/
COPY --from=builder /app/node_modules/ /app/node_modules/
COPY --from=builder /app/dist/ /app/dist/
COPY --from=builder /app/bin/ /app/bin/

ENTRYPOINT APP_VERSION=$(/app/bin/get_version_script.sh) node dist/src/main.js

Key points

  • --frozen-lockfile: the build fails when it does not match yarn.lock → guarantees a reproducible build
  • USER node: runs without root privileges → improves container security
  • libaio libnsl libc6-compat: additional libraries for cases that require native bindings such as Oracle DB
  • ENTRYPOINT: runs the application with its version injected as an environment variable

Build and run

# Build the image
docker build -t my-nestjs-app .

# Run (port 3000)
docker run -p 3000:3000 my-nestjs-app

# Inject environment variables
docker run -p 3000:3000 -e DATABASE_URL=... my-nestjs-app