NestJS Docker Setup
Docker packages your NestJS application and all its dependencies into a container — a self-contained unit that runs identically on any machine. "It works on my laptop" stops being an excuse when your app ships inside a Docker container. Docker also makes it simple to run a database alongside your app during development without installing anything globally.
What Docker Provides
Without Docker: Developer A: Node 18, PostgreSQL 14, runs fine Developer B: Node 20, PostgreSQL 16, different behavior Production: Node 16, PostgreSQL 15, crashes With Docker: Everyone runs the same container image Identical environment everywhere: dev, staging, production No "works on my machine" problems
Creating a Dockerfile
A Dockerfile describes how to build an image of your application. Use a multi-stage build to keep the production image small:
# Stage 1: Build the TypeScript code FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Stage 2: Run only the compiled output FROM node:20-alpine AS production WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev # install only production dependencies COPY --from=builder /app/dist ./dist EXPOSE 3000 CMD ["node", "dist/main"]
Multi-Stage Build Diagram
Stage 1 (builder): ├── All node_modules (dev + prod) ~500MB ├── src/ (TypeScript source) └── dist/ (compiled JavaScript) Stage 2 (production image): ├── node_modules (production only) ~100MB └── dist/ (compiled JavaScript) Final image: ~150MB instead of ~600MB No TypeScript compiler, no test libraries, no dev tools in production
Building and Running the Docker Image
# Build the image docker build -t my-nestjs-app . # Run the container docker run -p 3000:3000 \ -e DB_HOST=host.docker.internal \ -e DB_PORT=5432 \ -e DB_USER=postgres \ -e DB_PASS=secret \ -e JWT_SECRET=mysupersecret \ my-nestjs-app # App is now accessible at http://localhost:3000
Docker Compose for Development
Docker Compose runs multiple containers together — your NestJS app plus its database — with one command:
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- '3000:3000'
environment:
DB_HOST: db
DB_PORT: 5432
DB_USER: postgres
DB_PASS: secret
DB_NAME: myapp
JWT_SECRET: supersecretkey
depends_on:
db:
condition: service_healthy
volumes:
- ./src:/app/src # hot-reload in development
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
POSTGRES_DB: myapp
ports:
- '5432:5432'
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ['CMD', 'pg_isready', '-U', 'postgres']
interval: 5s
timeout: 5s
retries: 5
volumes:
postgres_data:
Docker Compose Commands
docker compose up # start all services docker compose up -d # start in background (detached) docker compose down # stop and remove containers docker compose down -v # stop and remove containers + volumes (deletes DB data) docker compose logs -f # stream logs from all services docker compose ps # show running services
.dockerignore File
The .dockerignore file prevents unnecessary files from being copied into the Docker build context, making builds faster:
node_modules dist .env .env.* *.spec.ts test/ .git README.md
Environment Variables in Docker
Pass environment variables to containers without hardcoding them in docker-compose.yml. Create a .env file and reference it:
# docker-compose.yml
services:
app:
env_file:
- .env # Docker reads from this file automatically
The .env file stays out of Docker images (excluded by .dockerignore) and out of version control (excluded by .gitignore). Each environment — development, staging, production — has its own .env file with the appropriate credentials.
Production vs Development Compose Files
docker-compose.yml # base config (shared settings) docker-compose.dev.yml # development overrides (hot-reload, debug port) docker-compose.prod.yml # production overrides (no volumes, restart policy) # Run development environment: docker compose -f docker-compose.yml -f docker-compose.dev.yml up # Run production environment: docker compose -f docker-compose.yml -f docker-compose.prod.yml up
Docker eliminates environment inconsistency from your entire development and deployment pipeline. Every developer, every CI server, and every production node runs the same container image. Onboarding a new developer goes from a half-day setup process to running docker compose up and being productive in minutes.
