I wrote about this exact topic earlier:

Dockerize Nestjs app with Postgres + Redis + Prisma ORM
Manuchehr ・ Dec 28 '23
Dockerfile
, in this post I'm going to cover secure general Dockerization for Nestjs. Let's create a Dockerfile
# Stage 1: Build the app FROM node:22-alpine AS builder WORKDIR /usr/src/app COPY package*.json ./ COPY entrypoint.sh ./ COPY .env ./ RUN yarn install --omit=dev COPY . . RUN yarn build # Stage 2: Setup prod FROM node:22-alpine WORKDIR /usr/src/app COPY --from=builder /usr/src/app/dist ./dist COPY --from=builder /usr/src/app/entrypoint.sh ./ COPY --from=builder /usr/src/app/package*.json ./ COPY --from=builder /usr/src/app/.env ./ ENV NODE_ENV production RUN chmod +x ./entrypoint.sh USER node ENTRYPOINT ["./entrypoint.sh"] CMD ["node", "dist/main.js"]
As you can see, Dockerfile
consists of two stages: Build & Prod. We need this to minimize build size by only copying essential files or directories (dist) to the final stage. I'm not going deep into Dockerfile
in this post, but I'll explain some of the lines:
RUN yarn install --omit=dev
: Install only production dependencies possible. Read hereentrypoint.sh
it's abash
file containing commands run before our Nestjs application. For example, you may have to run db migrations before Nestjs starts check my previous post.
⚠️ REMEMBER: If you use dev commands in
entrypoint.sh
file, make sure to install dev dependencies by removing--omit=dev
flag fromyarn install
. Also you need to copynode_modules
to final stage.
ENV NODE_ENV production
: Always run your Nestjs app onproduction
environment. When you build your Node.js Docker image for production, you want to ensure that all frameworks and libraries are using the optimal settings for performance and security. Read hereUSER node
: Usenode
usernode:22-alpine
image provides. Because you really don't want to run your app asroot
user. Read hereCMD ["node", "dist/main.js"]
: It's good way to run your node/nestjs application with directlynode
command instead ofnpm run start:prod
(don't do that like I did in my prev post :D). Read here
Bonus
Docker services (Postgres & Redis) setup
Create docker-compose.yaml
file:
services: app: container_name: 'nestjs-app' restart: always build: context: . dockerfile: Dockerfile environment: REDIS_HOST: redis DB_HOST: postgres PORT: ${PORT} ports: - ${PORT}:${PORT} networks: - nestjs-net depends_on: redis: condition: service_healthy restart: true postgres: condition: service_healthy restart: true redis: container_name: 'nestjs-redis' image: bitnami/redis:7.4 restart: always ports: - ${REDIS_PORT}:${REDIS_PORT} environment: REDIS_PASSWORD: ${REDIS_PASSWORD} REDIS_PORT_NUMBER: ${REDIS_PORT} REDIS_DB: ${REDIS_DB} REDIS_IO_THREADS: 4 REDIS_IO_THREADS_DO_READS: yes REDIS_DISABLE_COMMANDS: FLUSHDB,FLUSHALL healthcheck: test: ['CMD', 'redis-cli', '--raw', 'incr', 'ping'] interval: 5s timeout: 5s retries: 5 volumes: - nestjs-redis-data:/bitnami/redis/data networks: - nestjs-net postgres: container_name: 'nestjs-postgres' image: bitnami/postgresql:17 restart: always environment: POSTGRESQL_PORT_NUMBER: ${DB_PORT} POSTGRESQL_USERNAME: ${DB_USER} POSTGRESQL_PASSWORD: ${DB_PASSWORD} POSTGRESQL_DATABASE: ${DB_NAME} POSTGRESQL_TIMEZONE: 'Asia/Tashkent' // Set your timezone healthcheck: test: ['CMD-SHELL', 'pg_isready -U postgres'] interval: 5s timeout: 5s retries: 5 ports: - ${DB_PORT}:${DB_PORT} volumes: - nestjs-postgres-data:/bitnami/postgresql networks: - nestjs-net volumes: nestjs-redis-data: driver: local nestjs-postgres-data: driver: local networks: nestjs-net: driver: bridge
Example .env
file:
PORT=9000 # Redis REDIS_PASSWORD= REDIS_HOST=localhost REDIS_PORT=6379 REDIS_DB=0 # Postgres DB_USER= DB_PASSWORD= DB_NAME= DB_PORT=5432 DB_HOST=localhost
That's it! Thanks for reading. I don't say it's 100% secure Docker image because you can always implement something better. If you find any mistake please leave a comment. For more visit: OWASP Node.js Docker Cheat Sheet
Top comments (0)