We're about to embark on an epic journey into the world of containerization. In this guide, we'll be turning a regular NestJS app into a Dockerized powerhouse, complete with PostgreSQL, Redis, and the magic touch of Prisma ORM.
First of all we should create Dockerfile
:
FROM node:18-alpine
WORKDIR /usr/src/app
COPY package*.json ./
COPY entrypoint.sh ./
RUN npm install
COPY . .
RUN chmod +x /usr/src/app
RUN chmod +x /usr/src/app/entrypoint.sh
RUN npx prisma generate --schema=/usr/src/app/src/database/schema.prisma
RUN npm run build
ENTRYPOINT ["/usr/src/app/entrypoint.sh", "npm", "run", "start:prod"]
CMD ["npm", "run", "start:prod"]
FROM node:18-alpine
- This line sets the base image for the Docker image. It specifies that the image should be based on the official Node.js 18 Alpine image. Alpine is a lightweight Linux distribution, making the resulting Docker image smaller.WORKDIR /usr/src/app
- This line sets the working directory inside the container to/usr/src/app
. Subsequent commands will be executed from this directory.COPY package*.json ./
- Copies thepackage.json
andpackage-lock.json
files from the host machine to the/usr/src/app
directory inside the container. This allows for efficient installation of Node.js dependencies.COPY entrypoint.sh ./
- Copies theentrypoint.sh
script from the host machine to the/usr/src/app
directory inside the container. This script might be used to perform additional setup or configuration before starting the application.RUN npm install
- Executes thenpm install
command inside the container, installing the Node.js dependencies specified in thepackage.json
file. This step is crucial for setting up the application environment.COPY . ./
- Copies the entire application source code from the host machine to the/usr/src/app
directory inside the container. This includes all the application files, not just thepackage.json
andpackage-lock.json
files.RUN npx prisma generate --schema=/usr/src/app/src/database/schema.prisma
- Executes the Prisma CLI command to generate Prisma client code based on the schema defined in/usr/src/app/src/database/schema.prisma
. This step is specific to Prisma and is part of the database setup.RUN npm run build
- Executes thenpm run build
command inside the container. This command is often used to compile TypeScript code or perform other build-related tasks for the Node.js application.ENTRYPOINT ["/usr/src/app/entrypoint.sh", "npm", "run", "start:prod"]
- Specifies the default command to run when the container starts. In this case, it uses theentrypoint.sh
script followed by the commandnpm run start:prod
. Theentrypoint.sh
script handles additional setup before running the application.CMD ["npm", "run", "start:prod"]
- Provides a default command to run when the container starts if no other command is specified. This is overridden by theENTRYPOINT
command. Here, it's set to runnpm run start:prod
.
by following .dockerignore
file:
Dockerfile
.dockerignore
node_modules
npm-debug.log
dist
You should also create entrypoint.sh
this file is executed once docker image is started to make prisma migrations:
#!/bin/sh
# Apply Prisma migrations and start the application
npx prisma migrate deploy --schema=/usr/src/app/src/database/schema.prisma
npx prisma generate --schema=/usr/src/app/src/database/schema.prisma
# Run database migrations
npx prisma migrate dev --name init --schema=/usr/src/app/src/database/schema.prisma
# Run the main container command
exec "$@"
Attention! You should specify the path of your
prisma.schema
file.
Create a docker-compose.yml
file:
version: "3"
services:
app:
container_name: "myapp_web_app"
restart: unless-stopped
build:
context: .
dockerfile: Dockerfile
environment:
PORT: ${PORT}
POSTGRES_HOST: postgres_db # localhost doesn't work in docker container, hence you should use postgres container name instead of localhost
REDIS_HOST: redis # the same for redis. Use redis container name instead of localhost
ports:
- ${PORT}:${PORT}
depends_on:
- postgres_db
- redis
networks:
myapp_net:
postgres_db:
image: bitnami/postgresql:15
container_name: "myapp_postgres_db"
restart: unless-stopped
environment:
POSTGRESQL_USERNAME: ${POSTGRES_USER}
POSTGRESQL_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRESQL_DATABASE: ${POSTGRES_DB}
POSTGRESQL_TIMEZONE: "Asia/Tashkent" # This is optional
ports:
- ${POSTGRES_PORT}:5432
volumes:
- "postgres_data:/bitnami/postgresql"
networks:
myapp_net:
redis:
image: "bitnami/redis:7.2"
container_name: "myapp_redis"
restart: unless-stopped
ports:
- ${REDIS_PORT}:${REDIS_PORT}
volumes:
- "redis_data:/bitnami/redis/data"
environment:
REDIS_PASSWORD: ${REDIS_PASSWORD}
REDIS_PORT: ${REDIS_PORT}
REDIS_DB: ${REDIS_DB}
REDIS_IO_THREADS: 4
REDIS_IO_THREADS_DO_READS: yes
REDIS_DISABLE_COMMANDS: FLUSHDB,FLUSHALL
networks:
myapp_net:
volumes:
postgres_data:
redis_data:
networks:
myapp_net:
This is my .env
file:
PORT=9000
# Postgres
POSTGRES_DB=sampledb
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_USER=username
POSTGRES_PASSWORD=password
DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}?schema=public
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=veryhardpassword
REDIS_DB=0
Run docker compose
sudo docker compose up -d --build
For windows users:
docker-compose up -d --build
That’s all! I apologize for any mistake I made. If you find any mistake, please let me know.
I hope it’s helpful
Top comments (0)