Set Up PostgreSQL with NestJS & Docker for Fast Local Development

Launching a new project and need Postgres for NestJS development, but don’t want to commit to a production DB provider (yet)? Running a local Postgres instance in Docker is your best friend. Simple. Reliable. No system clutter. Below, I’ll walk you through my go-to setup for spinning up NestJS and PostgreSQL using Docker - minimal friction, fully scriptable, always reproducible. You’ll get practical configuration, commands for direct container access, and a sample NestJS database config. Why This Setup? Early development is all about moving fast: changing schemas, resetting data, running migrations, sometimes all in the same day. Managed cloud databases (like Neon) are a great final destination, but for local hacking and testing, Docker wins every time. It keeps Postgres off your host machine, avoids “works on my machine” surprises. This is true plug-and-play for local dev. Project Structure and Required Files Here’s what we’ll set up: Dockerfile for the NestJS app docker-compose.yml to wire up Node and Postgres .env file for environment variables Sample NestJS config and scripts Practical commands for common workflows Dockerfile: Simple Node Environment FROM node:18 WORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 3000 CMD ["npm", "run", "start:dev"] docker-compose.yml: Node + Postgres Side-by-Side This is the magic sauce that glues your Node API and a disposable Postgres instance together version: "3.8" services: db: image: postgres:13 restart: always env_file: - .env ports: - "5432:5432" volumes: - db-data:/var/lib/postgresql/data api: build: context: . dockerfile: Dockerfile ports: - "3000:3000" depends_on: - db env_file: - .env command: sh -c "npm run migration:run && npm run start:dev" volumes: db-data: Tip: The volumes key lets your database survive reboots without losing data. .env Create a .env file at your project root: POSTGRES_USER=postgres POSTGRES_PASSWORD=changeme POSTGRES_DB=app_db POSTGRES_HOST=db POSTGRES_PORT=5432 PORT=3000 Keep your secrets out of Git! .env goes in .gitignore. Package.json Scripts: Interactive Containers Why remember container IDs? Add this to your package.json scripts for quick access: "scripts": { "db": "docker exec -it $(docker-compose ps -q db) bash", "api": "docker exec -it $(docker-compose ps -q api) bash" } Now just run npm run db for a database container shell, or npm run api for the app. NestJS: Connecting to Your Dockerized Database In your main startup (e.g. main.ts): async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(process.env.PORT); } bootstrap(); Database Config: Here’s a common config file for TypeORM : const config = { type: "postgres", host: process.env.POSTGRES_HOST, port: parseInt(process.env.POSTGRES_PORT, 10), username: process.env.POSTGRES_USER, password: process.env.POSTGRES_PASSWORD, database: process.env.POSTGRES_DB, entities: [__dirname + "/**/*.entity{.ts,.js}"], synchronize: false, // safer for non-prod migrations: [__dirname + "/migrations/**/*{.ts,.js}"], autoLoadEntities: true, }; Development Workflow: Day-to-Day Commands Start everything:docker-compose up --build (first time) or just docker-compose up View logs:docker-compose logs -f api Tear it down (remove containers):docker-compose down Hop in the DB shell:npm run db Hop in app container:npm run api Conclusion - Start Fast, Move Faster

May 6, 2025 - 16:40
 0
Set Up PostgreSQL with NestJS & Docker for Fast Local Development

Launching a new project and need Postgres for NestJS development, but don’t want to commit to a production DB provider (yet)? Running a local Postgres instance in Docker is your best friend. Simple. Reliable. No system clutter.

Below, I’ll walk you through my go-to setup for spinning up NestJS and PostgreSQL using Docker - minimal friction, fully scriptable, always reproducible. You’ll get practical configuration, commands for direct container access, and a sample NestJS database config.

Why This Setup?

Early development is all about moving fast: changing schemas, resetting data, running migrations, sometimes all in the same day. Managed cloud databases (like Neon) are a great final destination, but for local hacking and testing, Docker wins every time. It keeps Postgres off your host machine, avoids “works on my machine” surprises. This is true plug-and-play for local dev.

Project Structure and Required Files

Here’s what we’ll set up:

  • Dockerfile for the NestJS app
  • docker-compose.yml to wire up Node and Postgres
  • .env file for environment variables
  • Sample NestJS config and scripts
  • Practical commands for common workflows

Dockerfile: Simple Node Environment

FROM node:18

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 3000

CMD ["npm", "run", "start:dev"]

docker-compose.yml: Node + Postgres Side-by-Side

This is the magic sauce that glues your Node API and a disposable Postgres instance together

version: "3.8"

services:
  db:
    image: postgres:13
    restart: always
    env_file:
      - .env
    ports:
      - "5432:5432"
    volumes:
      - db-data:/var/lib/postgresql/data

  api:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    depends_on:
      - db
    env_file:
      - .env
    command: sh -c "npm run migration:run && npm run start:dev"  

volumes:
  db-data:

Tip: The volumes key lets your database survive reboots without losing data.

.env

Create a .env file at your project root:

POSTGRES_USER=postgres
POSTGRES_PASSWORD=changeme
POSTGRES_DB=app_db
POSTGRES_HOST=db
POSTGRES_PORT=5432
PORT=3000

Keep your secrets out of Git! .env goes in .gitignore.

Package.json Scripts: Interactive Containers

Why remember container IDs? Add this to your package.json scripts for quick access:

"scripts": {
  "db": "docker exec -it $(docker-compose ps -q db) bash",
  "api": "docker exec -it $(docker-compose ps -q api) bash"
}

Now just run npm run db for a database container shell, or npm run api for the app.

NestJS: Connecting to Your Dockerized Database

In your main startup (e.g. main.ts):

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(process.env.PORT);
}
bootstrap();

Database Config:

Here’s a common config file for TypeORM :

const config = {
  type: "postgres",
  host: process.env.POSTGRES_HOST,
  port: parseInt(process.env.POSTGRES_PORT, 10),
  username: process.env.POSTGRES_USER,
  password: process.env.POSTGRES_PASSWORD,
  database: process.env.POSTGRES_DB,
  entities: [__dirname + "/**/*.entity{.ts,.js}"],
  synchronize: false,  // safer for non-prod
  migrations: [__dirname + "/migrations/**/*{.ts,.js}"],
  autoLoadEntities: true,
};

Development Workflow: Day-to-Day Commands

  • Start everything:docker-compose up --build (first time) or just docker-compose up
  • View logs:docker-compose logs -f api
  • Tear it down (remove containers):docker-compose down
  • Hop in the DB shell:npm run db
  • Hop in app container:npm run api

Conclusion - Start Fast, Move Faster