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)