Dockerize Nestjs app with Postgres + Redis + Prisma ORM
Tags: docker, nestjs, devops, prisma
Published: 2023-12-28 13:04:26

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`:
```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"]
```

1. **`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.

2. **`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.

3. **`COPY package*.json ./`** - Copies the `package.json` and `package-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.

4. **`COPY entrypoint.sh ./`** - Copies the `entrypoint.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.

5. **`RUN npm install`** - Executes the `npm install` command inside the container, installing the Node.js dependencies specified in the `package.json` file. This step is crucial for setting up the application environment.

6. **`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 the `package.json` and `package-lock.json` files.

7. **`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.

8. **`RUN npm run build`** - Executes the `npm 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.

9. **`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 the `entrypoint.sh` script followed by the command `npm run start:prod`. The `entrypoint.sh` script handles additional setup before running the application.

10. **`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 the `ENTRYPOINT` command. Here, it's set to run `npm run start:prod`.

by following `.dockerignore` file:
```.dockerignore
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:
```sh
#!/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:
```yaml
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:
```env
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
```bash
sudo docker compose up -d --build
```
For windows users:
```bash
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__