A Complete Guide to Using Prisma 7 with Docker and Docker Compose in NestJS
Source: Dev.to
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 with a containerised database—can be surprisingly tricky.
TL;DR – I spent almost two days debugging why Prisma could not connect to a PostgreSQL database running in Docker. This guide walks you through a step‑by‑step setup, common pitfalls, and best practices for running migrations in a containerised environment.
📋 Prerequisites
- Basic knowledge of NestJS
- Basic understanding of Docker and Docker‑Compose
- Docker Desktop installed and running
- A code editor (e.g., VS Code)
What is Prisma?
Prisma is an open‑source ORM (Object‑Relational Mapper) that simplifies database operations by mapping tables to JavaScript/TypeScript objects. Instead of writing raw SQL, you interact with your database using type‑safe APIs.
// Raw SQL
SELECT * FROM users WHERE email = 'john@example.com';
// Prisma
const user = await prisma.user.findUnique({
where: { email: 'john@example.com' },
});
Benefits
- Improves readability
- Reduces runtime errors
- Provides excellent TypeScript support
Prisma 7 Highlights (relevant for Docker)
| Feature | Why it matters for Docker |
|---|---|
| Rust‑free client engine | Fewer binary‑compatibility issues, especially with Alpine images |
| Fewer type definitions | Faster TypeScript compilation, better editor performance |
| Modern JavaScript support | Aligns with newer Node.js runtimes |
| Cleaner developer experience | Simplified configuration, fewer edge‑case errors |
Minimum requirements
- Node.js 20.19.0+
- TypeScript 5.4.0+
Why Docker?
Docker packages your application and its dependencies into containers, guaranteeing that it runs the same everywhere.
Benefits
- Consistent environments across teams
- Faster onboarding
- Easier deployment & scaling
- Fewer “works on my machine” issues
NestJS Overview
NestJS is a framework for building efficient, scalable Node.js server‑side applications. It’s built with TypeScript and supports:
- Object‑Oriented Programming (OOP)
- Functional Programming (FP)
- Functional Reactive Programming (FRP)
Under the hood it uses Express by default (or Fastify if you prefer).
Install the NestJS CLI globally
npm install -g @nestjs/cli
Create a new project
nest new backend
Scaffolds:
- TypeScript configuration
- Project structure
- Core dependencies
Generate controllers/services as needed:
nest g controller users
nest g service users
Adding Prisma to the NestJS Project
1️⃣ Install Prisma packages
# Development dependencies
npm install prisma @types/pg --save-dev
# Runtime dependencies
npm install @prisma/client @prisma/adapter-pg pg dotenv
2️⃣ Initialise Prisma
npx prisma init
Creates a prisma/ directory with schema.prisma and a .env file.
3️⃣ Configure the Prisma client generator (Prisma 7)
Add to prisma/schema.prisma (or a separate prisma.config.ts):
generator client {
provider = "prisma-client"
output = "../src/generated/prisma"
moduleFormat = "cjs"
}
4️⃣ Create a NestJS Prisma module & service
nest g module prisma
nest g service prisma
src/prisma/prisma.service.ts
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 service:
- Creates the
prisma/directory and generatesschema.prisma- Adds a
.envfile (if missing)- Generates a
prisma.config.ts(new in Prisma 7)
Dockerising PostgreSQL (stand‑alone container)
Tip: Make sure Docker Desktop is running before proceeding.
1️⃣ Create docker-compose.yml 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:
2️⃣ Bring the DB up
docker compose up -d
3️⃣ Set the connection string
Add to .env (or replace existing DATABASE_URL):
DATABASE_URL="postgresql://postgres:backend@localhost:5432/postgres?schema=public"
Note: If
localhostdoesn’t work on your machine, use127.0.0.1.
4️⃣ Run the first migration & generate the client
npx prisma migrate dev --name initial-migration
npx prisma generate
5️⃣ Start the NestJS app & open Prisma Studio
npm run start # or npm run dev
npx prisma studio
You should now see a working NestJS API backed by Prisma and a Dockerised PostgreSQL instance.
Building a Docker Image for the NestJS Server
Create a Dockerfile in the project root. Both Alpine‑based and Slim‑based images are supported by Prisma 7.
Example Dockerfile (Alpine)
# Use the LTS Alpine image (lightweight)
FROM node:lts-alpine
# Set working directory
WORKDIR /usr/src/app
# Install production dependencies
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
# Copy source code
COPY . .
# Run DB migrations then start the server
CMD ["sh", "-c", "npm run db:deploy && npm run dev"]
Alternative base images
node:lts-slim– slightly larger but more stablenode:lts– full Debian‑based image
Adding the Server to docker‑compose.yml
Extend the existing file with a server service that builds the Dockerfile.
services:
postgres:
# ... (same as before)
server:
build:
context: .
dockerfile: Dockerfile
depends_on:
postgres:
condition: service_healthy
environment:
DATABASE_URL: "postgresql://postgres:backend@postgres:5432/postgres?schema=public"
ports:
- "3000:3000"
networks:
- prisma-network
command: ["sh", "-c", "npm run db:deploy && npm run start:prod"]
Key points
depends_onensures the DB is healthy before the NestJS app starts.DATABASE_URLpoints to the service name (postgres) inside the Docker network, notlocalhost.
Bring everything up
docker compose up -d
Access the NestJS API at http://localhost:3000 and Prisma Studio at http://localhost:5555 (if exposed).
Common Pitfalls & How to Avoid Them
| Symptom | Likely Cause | Fix |
|---|---|---|
PrismaClientInitializationError: Unable to connect to database | Wrong host (localhost instead of service name) | Use postgres (service name) in DATABASE_URL when running inside Docker |
binary not found or unsupported platform | Using an Alpine image with the old Rust‑based engine | Upgrade to Prisma 7 (Rust‑free) or switch to a non‑Alpine base |
| Migrations keep failing | DB not ready when migration runs | Use Docker healthchecks + depends_on with condition: service_healthy |
| TypeScript compilation slows down | Too many type definitions (pre‑Prisma 7) | Upgrade to Prisma 7 – fewer definitions, faster builds |
Recap
- Set up NestJS and add Prisma.
- Create a Dockerised PostgreSQL service with healthchecks.
- Configure the Prisma client to use the Docker network host.
- Run migrations (
npx prisma migrate dev) before starting the server. - Dockerise the NestJS app with a lightweight Node image.
- Compose everything with
docker‑compose.ymland bring it up.
You now have a reproducible, container‑first development environment where Prisma 7, Docker, and NestJS work together seamlessly. Happy coding! 🚀
Docker Compose Setup
services:
app:
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
Environment Files
Create two separate files for each environment:
.env.dev.env.prod
Both should contain the database connection string:
DATABASE_URL="postgresql://postgres:prisma@postgres_db:5432/postgres?schema=public"
Running the Containers
-
Detached mode (reduced logs)
docker compose up --build -d -
Full logs
docker compose up --build
Tip: Adding the
-dflag runs the containers in detached mode, which suppresses most of the log output.
Common Pitfalls & Fixes
| Issue | Fix |
|---|---|
Inside Docker, localhost refers to the container itself. | Use the Docker service name instead: postgres_db:5432. |
| Containers may start before PostgreSQL is ready. | Add a wait script or use Docker health checks before running migrations. |
| Prisma Client isn’t generated inside the container, causing crashes. | Run npx prisma generate during the Docker build step. |
| Older Prisma versions have trouble with Alpine images. | Upgrade to Prisma 7 – its Rust‑free client engine greatly reduces this problem. |
Why Use Prisma 7 with Docker & Docker‑Compose?
- Reliable migrations – health checks ensure the DB is ready before applying them.
- Environment‑agnostic – separate
.envfiles prevent bugs that only appear in one environment. - Scalable workflow – the same configuration works from local development to production.
If this guide helped you, feel free to like, comment, or share.
Explore the full example repository: