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';
You write:
const user = await prisma.user.findUnique({
where: { email: 'john@example.com' }
});
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
Create a new project:
nest new backend
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
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
Initialize Prisma
npx prisma init
In schema.prisma add these
generator client {
provider = "prisma-client"
output = "../src/generated/prisma"
moduleFormat = "cjs"
}
Create a Prisma module and configure the prisma adapter
nest g module prisma
nest g service prisma
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();
}
}
This command:
- Creates the
prisma/directory - Generates
schema.prisma - Creates a
.envfile (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:
Start the database container
docker compose up -d
Update your .env
DATABASE_URL="postgresql://postgres:prisma@localhost:5432/postgres?schema=public"
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
Generate Prisma Client
npx prisma generate
Start the NestJS server
npm run start
To inspect your database visually
npx prisma studio
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"]
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
For environment separation, create:
.env.dev.env.prod
In .env.prod:
DATABASE_URL="postgresql://postgres:prisma@postgres_db:5432/postgres?schema=public"
Build and run everything
# Build in detached mode (reduced logs)
docker compose up --build -d
# Build with full logs
docker compose up --build
Tip: Attaching the
-dflag 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
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
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)
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 🙌🏽