DEV Community

robson idongesit samuel
robson idongesit samuel

Posted on

A Complete Guide to Using Prisma 7 with Docker and Docker Compose in NestJS

If you have ever tried running Prisma inside Docker and ended up stuck with connection errors that made absolutely no sense, this article is for you.

Modern backend applications often rely on ORMs such as Prisma, TypeORM, or Mongoose for database access, alongside Docker for consistent development environments. While each tool works great on its own, combining Prisma with Docker especially in a containerized database setup can be surprisingly tricky.

Speaking from experience, I spent almost two days debugging why Prisma could not connect to a PostgreSQL database running in Docker. In this article, we will walk through a step by step guide to using Prisma 7 with Docker and Docker Compose in a NestJS application. We will cover setup, common pitfalls, and best practices for running migrations in a containerized environment.

Prerequisites

Before getting started, you should have:

  • Basic knowledge of NestJS
  • Basic understanding of Docker and Docker Compose
  • Docker Desktop installed
  • A code editor such as VS Code

Technologies Used

Prisma

Prisma is an open-source ORM (Object-Relational Mapper) that simplifies database operations by mapping database tables to JavaScript and TypeScript objects. Instead of writing raw SQL queries, you interact with your database using type safe APIs.

Instead of writing:

SELECT * FROM users WHERE email = 'john@example.com';
Enter fullscreen mode Exit fullscreen mode

You write:

const user = await prisma.user.findUnique({
  where: { email: 'john@example.com' }
});
Enter fullscreen mode Exit fullscreen mode

This improves readability, reduces runtime errors, and provides excellent TypeScript support.

What Changed in Prisma 7 (and Why It Matters)

Prisma 7 introduced several important improvements that directly affect Docker-based setups:

  • Rust free Client Engine: Reduces binary compatibility issues, especially when using Alpine based Docker images.
  • Fewer type definitions: Faster TypeScript compilation and improved editor performance.
  • Modern JavaScript support: Better alignment with newer Node.js runtimes.
  • Cleaner developer experience: Simplified configuration and fewer edge case errors.

Minimum requirements:

  • Node.js 20.19.0+
  • TypeScript 5.4.0+

Docker

Docker is an open platform for developing, shipping, and running applications. It allows you to package your application and its dependencies into containers, ensuring your app runs the same way everywhere.

Benefits:

  • Consistent environments across teams
  • Faster onboarding
  • Easier deployment and scaling
  • Fewer "works on my machine" issues

NestJS

NestJS is a framework for building efficient and scalable Node.js server side applications. It is built with TypeScript and supports:

  • Object-Oriented Programming (OOP)
  • Functional Programming (FP)
  • Functional Reactive Programming (FRP)

Under the hood, NestJS uses Express by default and can also be configured with Fastify. It provides a clean abstraction while still exposing underlying APIs when needed.

Project Setup

Create a NestJS Application

Install the NestJS CLI globally:

npm install -g @nestjs/cli
Enter fullscreen mode Exit fullscreen mode

Create a new project:

nest new backend
Enter fullscreen mode Exit fullscreen mode

This command sets up:

  • TypeScript configuration
  • Project structure
  • Core dependencies

To generate controllers and services as needed:

nest g controller users
nest g service users
Enter fullscreen mode Exit fullscreen mode

Setting Up Prisma 7

Install Dependencies

In the backend folder, install the Prisma ORM:

npm install prisma @types/pg --save-dev
npm install @prisma/client @prisma/adapter-pg pg dotenv
Enter fullscreen mode Exit fullscreen mode

Initialize Prisma

npx prisma init
Enter fullscreen mode Exit fullscreen mode
In schema.prisma add these
generator client {
  provider     = "prisma-client"
  output       = "../src/generated/prisma"
  moduleFormat = "cjs"
}
Enter fullscreen mode Exit fullscreen mode

Create a Prisma module and configure the prisma adapter

nest g module prisma
nest g service prisma
Enter fullscreen mode Exit fullscreen mode
import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
import { PrismaPg } from '@prisma/adapter-pg';
import { PrismaClient } from 'src/generated/prisma/client';

@Injectable()
export class PrismaService
  extends PrismaClient
  implements OnModuleInit, OnModuleDestroy
{
  constructor() {
    const adapter = new PrismaPg({
      connectionString: process.env.DATABASE_URL,
    });
    super({ adapter, log: ['query', 'info', 'warn', 'error'] });
  }
  async onModuleInit() {
    await this.$connect();
  }
  async onModuleDestroy() {
    await this.$disconnect();
  }
}
Enter fullscreen mode Exit fullscreen mode

This command:

  • Creates the prisma/ directory
  • Generates schema.prisma
  • Creates a .env file (if it doesn't exist)
  • Adds prisma.config.ts (new in Prisma 7)

Running PostgreSQL with Docker Compose (Database Only)

Make sure Docker Desktop is running before continuing. To safely run migrations, we will first start PostgreSQL as a standalone container.

Create a docker-compose.yml file in the project root:

services:
  postgres:
    image: postgres:15
    restart: always
    environment:
      - POSTGRES_DB=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=backend
    ports:
      - "5432:5432"
    networks:
      - prisma-network
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
      interval: 5s
      timeout: 2s
      retries: 20
    volumes:
      - postgres_data:/var/lib/postgresql/data
    logging:
      options:
        max-size: "10m"
        max-file: "3"

networks:
  prisma-network:

volumes:
  postgres_data:
Enter fullscreen mode Exit fullscreen mode

Start the database container

docker compose up -d
Enter fullscreen mode Exit fullscreen mode

Update your .env

DATABASE_URL="postgresql://postgres:prisma@localhost:5432/postgres?schema=public"
Enter fullscreen mode Exit fullscreen mode

Note: Should localhost not work on your machine, use 127.0.0.1—it actually refers to your local machine's loopback interface.

Run migrations

npx prisma migrate dev --name initial-migration
Enter fullscreen mode Exit fullscreen mode

Generate Prisma Client

npx prisma generate
Enter fullscreen mode Exit fullscreen mode

Start the NestJS server

npm run start
Enter fullscreen mode Exit fullscreen mode

To inspect your database visually

npx prisma studio
Enter fullscreen mode Exit fullscreen mode

Running the App and Database Together with Docker Compose

Create a Dockerfile

Create a Dockerfile in your root folder. You can use either:

  • node:alpine (lighter and faster)
  • node:slim (slightly larger but more stable)

Both are supported by Prisma 7.

FROM node:lts-alpine

WORKDIR /usr/src/app

COPY package.json package-lock.json ./
RUN npm ci

COPY . .

CMD ["sh", "-c", "npm run db:deploy && npm run dev"]
Enter fullscreen mode Exit fullscreen mode

Docker Compose for App + Database

services:
  postgres_db:
    image: postgres:15
    hostname: postgres_db
    container_name: postgres_db
    restart: always
    environment:
      POSTGRES_DB: postgres
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: backend
    ports:
      - '5432:5432'
    networks:
      - prisma-network
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
      interval: 5s
      timeout: 2s
      retries: 20

  server:
    build: 
      context: .  
      dockerfile: Dockerfile
    ports:
      - '3000:3000'
    stdin_open: true
    tty: true 
    depends_on:
      postgres_db:
        condition: service_healthy
    env_file:
      - .env.prod
    networks:
      - prisma-network

networks:
  prisma-network:
    name: prisma-network
Enter fullscreen mode Exit fullscreen mode

For environment separation, create:

  • .env.dev
  • .env.prod

In .env.prod:

DATABASE_URL="postgresql://postgres:prisma@postgres_db:5432/postgres?schema=public"
Enter fullscreen mode Exit fullscreen mode

Build and run everything

# Build in detached mode (reduced logs)
docker compose up --build -d

# Build with full logs
docker compose up --build
Enter fullscreen mode Exit fullscreen mode

Tip: Attaching the -d flag means you're building in detached mode, which reduces some of the logs you will see.

Common Pitfalls When Using Prisma with Docker

These issues caused most of my debugging headaches:

1. Using localhost Inside Docker

Inside Docker, localhost refers to the container itself.

Fix: Use the Docker service name instead:

postgres_db:5432
Enter fullscreen mode Exit fullscreen mode

2. Running Migrations Before the Database Is Ready

Docker containers may start before PostgreSQL is ready to accept connections.

Fix: Use a wait script or Docker health checks before running migrations.

3. Forgetting to Regenerate Prisma Client

If Prisma Client is not generated inside the container, your app may crash.

Fix: Run this during the Docker build step:

npx prisma generate
Enter fullscreen mode Exit fullscreen mode

4. Alpine Image Compatibility Issues

Older Prisma versions struggled with Alpine images.

Fix: Prisma 7's Rust free client engine significantly reduces this issue.

Conclusion

Using Prisma 7 with Docker and Docker Compose provides a powerful and consistent backend development workflow. While the setup can be challenging especially around migrations and container networking understanding how Docker services communicate makes everything click.

With this setup, you can:

  • Run migrations reliably
  • Avoid environment-specific bugs
  • Scale smoothly from development to production

If this article helped you, feel free to like, comment, or share. You can also check out the full example on GitHub.
https://github.com/idongesit98/DockerProject

Top comments (1)

Collapse
 
faithomobude profile image
Faith Omobude

This is a well constructed documentation I must say. This is one of the clearest explanations I’ve seen around Prisma + Docker + NestJS, especially the migration flow and service name vs localhost pitfall. I do work with backend developers and this is really one issue that keep us up for hours on certain projects.

Also I appreciate you calling out Prisma 7’s Rust-free client engine and Alpine compatibility. That context is often skipped but makes a huge difference in real-world Docker builds.

One possible enhancement (especially for production readers like myself) could be briefly touching on separating migrate deploy vs migrate dev usage across environments or probably running migrations as a one-off job instead of inside the app container.

Just a thought tho, hopefully I get to read on them from you my chief 😌

Overall, this is a great practical guide. I'm definitely bookmarking this for future Prisma + Docker setups. Thanks for sharing your debugging lessons 🙌🏽