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)