This guide covers Docker usage for the NestJS API Starter Kit, including development, testing, and production deployments. The project includes comprehensive Docker configuration for consistent environments across all stages.
- Docker Overview
- Docker Architecture
- Development with Docker
- Production Deployment
- Docker Compose Services
- Multi-stage Dockerfile
- Environment Configuration
- Data Persistence
- Networking
- Health Checks
- Performance Optimization
- Troubleshooting
- Best Practices
Docker provides several advantages for this NestJS application:
- Consistency: Same environment across development, testing, and production
- Isolation: Dependencies and services are containerized
- Scalability: Easy horizontal scaling with orchestration
- Development Speed: Quick setup without installing dependencies locally
- CI/CD Integration: Consistent build and deployment process
Ensure you have the following installed:
# Check Docker version
docker --version
# Should be 20.0.0 or higher
# Check Docker Compose version
docker compose version
# Should be 2.0.0 or higherInstallation:
- macOS: Docker Desktop for Mac
- Windows: Docker Desktop for Windows
- Linux: Docker Engine
The project uses a multi-container architecture:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ NestJS API │────│ PostgreSQL │ │ Redis │
│ (Port 3000) │ │ (Port 5432) │ │ (Port 6379) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
└───────────────────────┼───────────────────────┘
│
┌─────────────────┐
│ Adminer │
│ (Port 8080) │
└─────────────────┘
- Development: Multi-stage build with development dependencies
- Production: Optimized image with only runtime dependencies
- Testing: Separate test environment with test database
# Clone the repository
git clone <repository-url>
cd nestjs-api-starter-kit
# Start all development services
docker compose up
# Or run in background
docker compose up -d
# View logs
docker compose logs -f api-
Initial Setup
# Build and start services docker compose up --build # Run database migrations docker compose exec api npm run typeorm:migration:run # Seed the database docker compose exec api npm run db:seed
-
Daily Development
# Start services docker compose up -d # Make code changes (hot reload is enabled) # Your changes will be reflected automatically # View API logs docker compose logs -f api # Execute commands in container docker compose exec api npm run lint docker compose exec api npm test
-
Development Commands
# Install new dependencies docker compose exec api npm install <package-name> docker compose restart api # Restart to pick up new dependencies # Run database operations docker compose exec api npm run typeorm:migration:generate -- -n NewMigration docker compose exec api npm run typeorm:migration:run # Access database docker compose exec postgres psql -U nestjs_user -d nestjs_starter # Shell access docker compose exec api sh
Development mode includes hot reloading through volume mounting:
# docker-compose.yml (development)
volumes:
- .:/usr/src/app # Mount source code
- /usr/src/app/node_modules # Preserve node_modules
- nestjs_logs:/usr/src/app/logsBenefits:
- Instant reflection of code changes
- No need to rebuild images during development
- Preserves installed node_modules
# VS Code with Docker
# Install "Dev Containers" extension
# Open Command Palette > "Dev Containers: Open Folder in Container"
# Debug in Docker
docker compose -f docker-compose.yml -f docker-compose.debug.yml up
# Run tests in Docker
docker compose exec api npm run test:all# Build production image
docker build --target production -t nestjs-api:latest .
# Or use npm script
npm run docker:build
# Test production image locally
docker run -p 3000:3000 --env-file .env.production nestjs-api:latest# docker-compose.production.yml
version: '3.8'
services:
api:
image: nestjs-api:latest
restart: unless-stopped
environment:
NODE_ENV: production
ports:
- '3000:3000'
depends_on:
- postgres
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:3000/health/live']
interval: 30s
timeout: 10s
retries: 3
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- '5432:5432'
volumes:
postgres_data:Docker Swarm:
# Initialize swarm
docker swarm init
# Deploy stack
docker stack deploy -c docker-compose.production.yml nestjs-app
# Scale services
docker service scale nestjs-app_api=3Kubernetes Deployment:
# k8s/deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nestjs-api
spec:
replicas: 3
selector:
matchLabels:
app: nestjs-api
template:
metadata:
labels:
app: nestjs-api
spec:
containers:
- name: api
image: nestjs-api:latest
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: 'production'
- name: DB_HOST
value: 'postgres-service'
livenessProbe:
httpGet:
path: /health/live
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5api:
build:
context: .
target: development
container_name: nestjs-api
restart: unless-stopped
ports:
- '3000:3000'
environment:
- NODE_ENV=development
- DB_HOST=postgres
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
- nestjs_logs:/usr/src/app/logs
depends_on:
postgres:
condition: service_healthy
networks:
- nestjs-networkpostgres:
image: postgres:16-alpine
container_name: nestjs-postgres
restart: unless-stopped
environment:
- POSTGRES_DB=nestjs_starter
- POSTGRES_USER=nestjs_user
- POSTGRES_PASSWORD=nestjs_password
ports:
- '5432:5432'
volumes:
- postgres_data:/var/lib/postgresql/data
- ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql:ro
networks:
- nestjs-network
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U nestjs_user -d nestjs_starter']
interval: 10s
timeout: 5s
retries: 5
start_period: 10sredis:
image: redis:7-alpine
container_name: nestjs-redis
restart: unless-stopped
ports:
- '6379:6379'
volumes:
- redis_data:/data
networks:
- nestjs-network
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
command: redis-server --appendonly yesadminer:
image: adminer:4.8.1
container_name: nestjs-adminer
restart: unless-stopped
ports:
- '8080:8080'
environment:
- ADMINER_DEFAULT_SERVER=postgres
- ADMINER_DESIGN=pepa-linha
depends_on:
postgres:
condition: service_healthy
networks:
- nestjs-network# Stage 1: Development
FROM node:20-alpine AS development
WORKDIR /usr/src/app
# Install system dependencies
RUN apk add --no-cache dumb-init curl && rm -rf /var/cache/apk/*
# Copy package files
COPY package*.json ./
# Install development dependencies
RUN npm ci --only=development
# Copy source code
COPY . .
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nestjs -u 1001 && \
chown -R nestjs:nodejs /usr/src/app
USER nestjs
EXPOSE 3000
ENTRYPOINT ["dumb-init", "--"]
CMD ["npm", "run", "start:dev"]
# Stage 2: Build
FROM node:20-alpine AS build
WORKDIR /usr/src/app
# Install build dependencies
RUN apk add --no-cache python3 make g++ && rm -rf /var/cache/apk/*
# Copy package files and configs
COPY package*.json tsconfig*.json nest-cli.json ./
# Install all dependencies
RUN npm ci --include=dev
# Copy source code
COPY src/ ./src/
# Build application
RUN npm run build
# Install production dependencies
RUN npm ci --only=production --ignore-scripts && npm cache clean --force
# Stage 3: Production
FROM node:20-alpine AS production
# Install runtime dependencies
RUN apk add --no-cache dumb-init curl && rm -rf /var/cache/apk/*
WORKDIR /usr/src/app
# Create non-root user
RUN addgroup -g 1001 -S nodejs && adduser -S nestjs -u 1001
# Copy from build stage
COPY --from=build --chown=nestjs:nodejs /usr/src/app/dist ./dist
COPY --from=build --chown=nestjs:nodejs /usr/src/app/node_modules ./node_modules
COPY --from=build --chown=nestjs:nodejs /usr/src/app/package*.json ./
# Create logs directory
RUN mkdir -p /usr/src/app/logs && chown nestjs:nodejs /usr/src/app/logs
USER nestjs
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health/live || exit 1
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/main.js"]-
Development Stage
- Includes development dependencies
- Enables hot reloading
- Suitable for local development
-
Build Stage
- Compiles TypeScript to JavaScript
- Installs production dependencies
- Optimizes for build process
-
Production Stage
- Minimal runtime image
- Only production dependencies
- Security-hardened
# Development (default)
docker compose up
# Testing
docker compose -f docker-compose.yml -f docker-compose.test.yml up
# Production
docker compose -f docker-compose.production.yml up
# Override specific services
docker compose -f docker-compose.yml -f docker-compose.override.yml up# .env.docker
NODE_ENV=development
PORT=3000
HOST=0.0.0.0
# Database
DB_HOST=postgres # Service name in Docker network
DB_PORT=5432
DB_USERNAME=nestjs_user
DB_PASSWORD=nestjs_password
DB_NAME=nestjs_starter
# Redis
REDIS_HOST=redis # Service name in Docker network
REDIS_PORT=6379
# Application
JWT_SECRET=your-super-secret-jwt-key
LOG_LEVEL=infoDocker Secrets (Swarm mode):
services:
api:
secrets:
- db_password
- jwt_secret
secrets:
db_password:
external: true
jwt_secret:
external: trueEnvironment File Security:
# Never commit .env files with real secrets
echo ".env*" >> .gitignore
# Use different env files per environment
docker compose --env-file .env.production up-
Named Volumes (Recommended)
volumes: postgres_data: driver: local
-
Bind Mounts (Development)
volumes: - ./data:/var/lib/postgresql/data
-
Anonymous Volumes
volumes: - /var/lib/postgresql/data
# Backup database
docker compose exec postgres pg_dump -U nestjs_user nestjs_starter > backup.sql
# Backup with Docker volume
docker run --rm -v nestjs_postgres_data:/data -v $(pwd):/backup alpine \
tar czf /backup/postgres_backup.tar.gz -C /data .
# Restore database
docker compose exec -T postgres psql -U nestjs_user nestjs_starter < backup.sql
# Restore volume
docker run --rm -v nestjs_postgres_data:/data -v $(pwd):/backup alpine \
tar xzf /backup/postgres_backup.tar.gz -C /data# Migrate data between environments
docker compose exec api npm run typeorm:migration:run
# Export data for migration
docker compose exec postgres pg_dump -U nestjs_user --data-only nestjs_starter > data_export.sqlnetworks:
nestjs-network:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.20.0.0/16Services can communicate using service names:
// Database connection in container
const connection = {
host: 'postgres', // Service name, not localhost
port: 5432,
username: 'nestjs_user',
password: 'nestjs_password',
database: 'nestjs_starter',
};# Host:Container port mapping
ports:
- '3000:3000' # API
- '5432:5432' # PostgreSQL
- '8080:8080' # Adminer
- '6379:6379' # Redis# Using nginx as load balancer
nginx:
image: nginx:alpine
ports:
- '80:80'
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- api# nginx.conf
upstream api {
server nestjs-api-1:3000;
server nestjs-api-2:3000;
server nestjs-api-3:3000;
}
server {
listen 80;
location / {
proxy_pass http://api;
}
}healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:3000/health/live']
interval: 30s
timeout: 10s
retries: 3
start_period: 40sdepends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy#!/bin/sh
# health-check.sh
set -e
# Check if application is responding
if ! curl -f http://localhost:3000/health/live; then
echo "Health check failed"
exit 1
fi
# Check database connectivity
if ! curl -f http://localhost:3000/health/ready; then
echo "Database connectivity check failed"
exit 1
fi
echo "Health check passed"
exit 0# Use multi-stage builds
FROM node:20-alpine AS build
# ... build steps
FROM node:20-alpine AS production
COPY --from=build /usr/src/app/dist ./dist# Copy package.json first for better caching
COPY package*.json ./
RUN npm ci --only=production
# Copy source code after dependencies
COPY . .
RUN npm run buildservices:
api:
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M# Use Alpine images
FROM node:20-alpine
# Multi-stage builds
FROM node:20-alpine AS build
# ... build steps
FROM node:20-alpine AS production
# Remove unnecessary files
RUN rm -rf /var/cache/apk/* \
&& npm cache clean --force
# Use .dockerignore
# .dockerignore
node_modules
coverage
*.md
.git-
Port Already in Use
# Find process using port lsof -ti:3000 # Kill process kill -9 $(lsof -ti:3000) # Or use different port docker compose up --scale api=1 -p 3001:3000
-
Permission Issues
# Fix ownership issues sudo chown -R $USER:$USER . # Or use user in Dockerfile RUN adduser -S nestjs -u 1001 USER nestjs
-
Out of Disk Space
# Clean up Docker system docker system prune -a # Remove unused volumes docker volume prune # Remove unused images docker image prune -a
-
Container Won't Start
# Check logs docker compose logs api # Check container status docker compose ps # Enter container for debugging docker compose exec api sh
# View container logs
docker compose logs -f api
# Execute commands in running container
docker compose exec api sh
# Check container resource usage
docker stats
# Inspect container configuration
docker compose config
# View network information
docker network ls
docker network inspect nestjs_nestjs-network
# Check volume information
docker volume ls
docker volume inspect nestjs_postgres_data# Test database connection
docker compose exec postgres psql -U nestjs_user -d nestjs_starter -c "SELECT version();"
# Check if database is ready
docker compose exec postgres pg_isready -U nestjs_user -d nestjs_starter
# View database logs
docker compose logs postgres-
Use Non-root Users
RUN adduser -S nestjs -u 1001 USER nestjs
-
Minimal Base Images
FROM node:20-alpine # Instead of node:20 -
Regular Updates
# Update base images regularly docker compose pull docker compose up --build -
Secrets Management
# Use Docker secrets or external secret management secrets: - db_password
-
Use .dockerignore
node_modules coverage .git *.md Dockerfile docker-compose*.yml -
Layer Optimization
# Copy package.json first COPY package*.json ./ RUN npm ci # Copy source code last COPY . .
-
Health Checks
healthcheck: test: ['CMD', 'curl', '-f', 'http://localhost:3000/health/live'] interval: 30s
-
Resource Limits
deploy: resources: limits: memory: 512M cpus: '1.0'
-
Restart Policies
restart: unless-stopped
-
Logging
logging: driver: 'json-file' options: max-size: '10m' max-file: '3'
-
Container Monitoring
# Monitor resource usage docker stats # Monitor logs docker compose logs -f --tail=100
-
Health Monitoring
# Check health status docker compose ps # Test health endpoints curl http://localhost:3000/health/live
This Docker guide provides comprehensive coverage of containerization for the NestJS API Starter Kit. Following these practices will ensure consistent, secure, and scalable deployments across all environments.
Next: Learn about Development Tooling to understand the integrated development tools and their configuration.