Skip to content

Deployment Guide ​

This comprehensive guide covers deployment strategies, configurations, and best practices for the Awesome NestJS Boilerplate across different environments and platforms.

Deployment Overview ​

The Awesome NestJS Boilerplate supports multiple deployment strategies:

  1. Docker Deployment: Containerized deployment with Docker and Docker Compose
  2. Traditional Deployment: Direct server deployment with PM2 process management
  3. Cloud Platforms: Managed deployment on AWS, GCP, Heroku, and other platforms
  4. CI/CD Integration: Automated deployment with GitHub Actions and GitLab CI

Environment Preparation ​

Production Environment Variables ​

Create a production .env file with the following variables:

env
# Application
NODE_ENV=production
PORT=3000

# Database
DB_TYPE=postgres
DB_HOST=your-db-host
DB_PORT=5432
DB_USERNAME=your-db-user
DB_PASSWORD=your-secure-password
DB_DATABASE=your-db-name
ENABLE_ORM_LOGS=false

# JWT Authentication (RSA key pair — RS256 algorithm)
# Generate with: openssl genpkey -algorithm RSA -out private.pem && openssl rsa -pubout -in private.pem -out public.pem
JWT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"

# CORS
CORS_ORIGINS=https://yourdomain.com,https://www.yourdomain.com

# API Documentation (disable in production)
ENABLE_DOCUMENTATION=false

# Throttling
THROTTLER_TTL=60
THROTTLER_LIMIT=100

# NATS (if using microservices)
NATS_ENABLED=false
NATS_HOST=your-nats-host
NATS_PORT=4222

# AWS S3 (if using file uploads)
AWS_S3_BUCKET_NAME=your-bucket-name
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_REGION=us-east-1

Database Configuration ​

Ensure your production database is properly configured:

sql
-- Create database and user
CREATE DATABASE your_db_name;
CREATE USER your_db_user WITH ENCRYPTED PASSWORD 'your_secure_password';
GRANT ALL PRIVILEGES ON DATABASE your_db_name TO your_db_user;

-- Grant schema permissions
GRANT ALL ON SCHEMA public TO your_db_user;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO your_db_user;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO your_db_user;

Docker Deployment ​

Prerequisites ​

  • Docker Engine 20.10+
  • Docker Compose 2.0+
  • Access to a container registry (optional)

Production Docker Setup ​

Dockerfile (multi-stage build, from the actual Dockerfile in the repo):

dockerfile
# Stage 1: base — enable pnpm via corepack
FROM node:25-slim AS base
RUN corepack enable

# Stage 2: build — install all deps, compile TypeScript
FROM base AS build
WORKDIR /app
COPY pnpm-lock.yaml package.json ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm run build:prod

# Stage 3: prod-deps — production-only dependencies
FROM base AS prod-deps
WORKDIR /app
COPY pnpm-lock.yaml package.json ./
RUN pnpm install --frozen-lockfile --prod

# Stage 4: runtime — lean final image
FROM node:25-slim AS runtime
WORKDIR /app
ENV NODE_ENV=production
ENV NODE_OPTIONS="--max-old-space-size=8192"
ARG PORT=3000
ARG secret_manager_arn
EXPOSE $PORT
COPY --from=build /app/dist ./dist
COPY --from=prod-deps /app/node_modules ./node_modules
CMD ["node", "dist/main.js"]

Docker Compose Deployment ​

The repo's docker-compose.yml provides the development stack. For production, adapt it or build your own. The development compose includes:

  • app: Builds from the multi-stage Dockerfile, depends on postgres (healthy)
  • postgres: Standard Postgres image with pg_isready health check and init-data.sh volume mount
  • pgadmin: dpage/pgadmin4, available at http://localhost:8080

Example production-focused override (docker-compose.prod.yml):

yaml
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_USERNAME=${DB_USERNAME}
      - DB_PASSWORD=${DB_PASSWORD}
      - DB_DATABASE=${DB_DATABASE}
      - JWT_PRIVATE_KEY=${JWT_PRIVATE_KEY}
      - JWT_PUBLIC_KEY=${JWT_PUBLIC_KEY}
      - CORS_ORIGINS=${CORS_ORIGINS}
    depends_on:
      postgres:
        condition: service_healthy
    restart: unless-stopped

  postgres:
    image: postgres:16-alpine
    environment:
      - POSTGRES_USER=${DB_USERNAME}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
      - POSTGRES_DB=${DB_DATABASE}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME} -d ${DB_DATABASE}"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

volumes:
  postgres_data:

Deployment commands:

bash
# Build and start services
docker-compose -f docker-compose.prod.yml up -d --build

# View logs
docker-compose -f docker-compose.prod.yml logs -f

# Scale the application
docker-compose -f docker-compose.prod.yml up -d --scale app=3

# Stop services
docker-compose -f docker-compose.prod.yml down

Container Registry ​

Build and push to registry:

bash
# Build image
docker build -t your-registry/nest-boilerplate:latest .

# Tag for versioning
docker tag your-registry/nest-boilerplate:latest your-registry/nest-boilerplate:v1.0.0

# Push to registry
docker push your-registry/nest-boilerplate:latest
docker push your-registry/nest-boilerplate:v1.0.0

Traditional Server Deployment ​

Prerequisites ​

  • Node.js 25+
  • pnpm 10.26+
  • PostgreSQL 14+
  • PM2 process manager
  • Nginx (recommended)

Build and Deploy ​

bash
# 1. Clone repository
git clone https://github.com/your-username/your-nest-app.git
cd your-nest-app

# 2. Install dependencies
pnpm install --frozen-lockfile

# 3. Build application
pnpm build:prod

# 4. Set up environment
cp .env.example .env
# Edit .env with production values

# 5. Run database migrations
pnpm migration:run

# 6. Start application
pnpm start:prod

Process Management ​

PM2 Configuration (ecosystem.config.js):

javascript
module.exports = {
  apps: [
    {
      name: 'nest-boilerplate',
      script: 'dist/main.js',
      instances: 'max',
      exec_mode: 'cluster',
      env: {
        NODE_ENV: 'production',
        PORT: 3000,
      },
      env_production: {
        NODE_ENV: 'production',
        PORT: 3000,
      },
      error_file: './logs/err.log',
      out_file: './logs/out.log',
      log_file: './logs/combined.log',
      time: true,
      max_memory_restart: '1G',
      node_args: '--max-old-space-size=1024',
    },
  ],
};

PM2 Commands:

bash
# Install PM2 globally
npm install -g pm2

# Start application
pm2 start ecosystem.config.js --env production

# Monitor processes
pm2 monit

# View logs
pm2 logs nest-boilerplate

# Restart application
pm2 restart nest-boilerplate

# Stop application
pm2 stop nest-boilerplate

# Save PM2 configuration
pm2 save

# Setup PM2 startup script
pm2 startup

Nginx Configuration (/etc/nginx/sites-available/nest-boilerplate):

nginx
server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}

Cloud Platform Deployment ​

AWS Deployment ​

AWS Elastic Beanstalk ​

.elasticbeanstalk/config.yml:

yaml
branch-defaults:
  main:
    environment: nest-boilerplate-prod
global:
  application_name: nest-boilerplate
  default_platform: Node.js 25
  default_region: us-east-1
  sc: git

Deployment commands:

bash
# Install EB CLI
pip install awsebcli

# Initialize EB application
eb init

# Create environment
eb create production

# Deploy
eb deploy

# Open application
eb open

AWS ECS with Fargate ​

task-definition.json:

json
{
  "family": "nest-boilerplate",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "512",
  "memory": "1024",
  "executionRoleArn": "arn:aws:iam::account:role/ecsTaskExecutionRole",
  "containerDefinitions": [
    {
      "name": "nest-app",
      "image": "your-account.dkr.ecr.region.amazonaws.com/nest-boilerplate:latest",
      "portMappings": [
        {
          "containerPort": 3000,
          "protocol": "tcp"
        }
      ],
      "environment": [
        {
          "name": "NODE_ENV",
          "value": "production"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/nest-boilerplate",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ]
}

Memory Requirements:

  • Minimum: 512MB (for basic applications without image processing)
  • Recommended: 1GB (for applications with image processing, file uploads, or heavy workloads)
  • Production: 2GB+ (for high-traffic applications with complex operations)

The Dockerfile includes NODE_OPTIONS="--max-old-space-size=8192" for 8GB containers, which limits Node.js heap to 100% of container memory, leaving room for system overhead. For larger containers, adjust this value accordingly (e.g., --max-old-space-size=16384 for 16GB containers).

Google Cloud Platform ​

app.yaml (App Engine):

yaml
runtime: nodejs25

env_variables:
  NODE_ENV: production
  DB_HOST: /cloudsql/project:region:instance
  JWT_PRIVATE_KEY: your-rsa-private-key
  JWT_PUBLIC_KEY: your-rsa-public-key

automatic_scaling:
  min_instances: 1
  max_instances: 10
  target_cpu_utilization: 0.6

resources:
  cpu: 1
  memory_gb: 0.5
  disk_size_gb: 10

Deployment:

bash
# Deploy to App Engine
gcloud app deploy

# View logs
gcloud app logs tail -s default

Heroku Deployment ​

Procfile:

web: node dist/main.js
release: pnpm migration:run

package.json scripts:

json
{
  "scripts": {
    "heroku-postbuild": "pnpm build:prod"
  }
}

Deployment commands:

bash
# Login to Heroku
heroku login

# Create application
heroku create your-app-name

# Add PostgreSQL addon
heroku addons:create heroku-postgresql:hobby-dev

# Set environment variables
heroku config:set NODE_ENV=production
heroku config:set JWT_PRIVATE_KEY="$(cat private.pem)"
heroku config:set JWT_PUBLIC_KEY="$(cat public.pem)"

# Deploy
git push heroku main

# Run migrations
heroku run pnpm migration:run

# View logs
heroku logs --tail

DigitalOcean App Platform ​

.do/app.yaml:

yaml
name: nest-boilerplate
services:
- name: api
  source_dir: /
  github:
    repo: your-username/nest-boilerplate
    branch: main
  run_command: pnpm start:prod
  environment_slug: node-js
  instance_count: 1
  instance_size_slug: basic-xxs
  env:
  - key: NODE_ENV
    value: production
  - key: JWT_PRIVATE_KEY
    value: your-rsa-private-key
    type: SECRET
  - key: JWT_PUBLIC_KEY
    value: your-rsa-public-key
    type: SECRET
databases:
- name: postgres-db
  engine: PG
  version: "13"
  size: db-s-dev-database

CI/CD Pipeline ​

GitHub Actions ​

.github/workflows/deploy.yml:

yaml
name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 10

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '25'
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Run tests
        run: pnpm test:cov

      - name: Run e2e tests
        run: pnpm test:e2e

  build-and-deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 10

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '25'
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Build application
        run: pnpm build:prod

      - name: Build Docker image
        run: |
          docker build -t ${{ secrets.DOCKER_REGISTRY }}/nest-boilerplate:${{ github.sha }} .
          docker tag ${{ secrets.DOCKER_REGISTRY }}/nest-boilerplate:${{ github.sha }} ${{ secrets.DOCKER_REGISTRY }}/nest-boilerplate:latest

      - name: Login to Docker Registry
        run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login ${{ secrets.DOCKER_REGISTRY }} -u ${{ secrets.DOCKER_USERNAME }} --password-stdin

      - name: Push Docker image
        run: |
          docker push ${{ secrets.DOCKER_REGISTRY }}/nest-boilerplate:${{ github.sha }}
          docker push ${{ secrets.DOCKER_REGISTRY }}/nest-boilerplate:latest

      - name: Deploy to production
        run: |
          # Add your deployment script here
          # e.g., kubectl apply, docker-compose pull && docker-compose up -d, etc.

GitLab CI ​

.gitlab-ci.yml:

yaml
stages:
  - test
  - build
  - deploy

variables:
  DOCKER_DRIVER: overlay2
  DOCKER_TLS_CERTDIR: "/certs"

test:
  stage: test
  image: node:25-slim
  before_script:
    - corepack enable
    - pnpm config set store-dir .pnpm-store
  cache:
    paths:
      - .pnpm-store/
  script:
    - pnpm install --frozen-lockfile
    - pnpm test:cov
    - pnpm test:e2e

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:latest
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - docker push $CI_REGISTRY_IMAGE:latest
  only:
    - main

deploy:
  stage: deploy
  image: alpine:latest
  script:
    - apk add --no-cache curl
    - curl -X POST $DEPLOY_WEBHOOK_URL
  only:
    - main

Database Migration ​

Production migration strategy:

bash
# 1. Backup database
pg_dump -h localhost -U username -d database_name > backup.sql

# 2. Run migrations
pnpm migration:run

# 3. Verify migration
pnpm migration:show

# 4. Rollback if needed (be careful!)
pnpm migration:revert

Zero-downtime migration approach:

  1. Deploy new version alongside old version
  2. Run migrations on new version
  3. Switch traffic to new version
  4. Remove old version

Security Considerations ​

Environment Security ​

  • Use environment variables for sensitive data
  • Never commit secrets to version control
  • Use secret management services (AWS Secrets Manager, etc.)
  • Implement proper RBAC for deployment access

Application Security ​

  • Enable HTTPS with valid SSL certificates
  • Configure proper CORS settings
  • Implement rate limiting
  • Use security headers (helmet.js)
  • Regular security audits (pnpm audit)

Infrastructure Security ​

  • Use private networks for database connections
  • Implement proper firewall rules
  • Regular security updates
  • Monitor for vulnerabilities

Monitoring and Logging ​

Application Monitoring ​

Health Check Endpoint:

typescript
// src/health-check.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function healthCheck() {
  try {
    const app = await NestFactory.create(AppModule);
    await app.listen(3000);
    console.log('Health check passed');
    process.exit(0);
  } catch (error) {
    console.error('Health check failed:', error);
    process.exit(1);
  }
}

healthCheck();

Logging Configuration:

typescript
// src/main.ts
import { Logger } from '@nestjs/common';

const logger = new Logger('Bootstrap');

// Log application startup
logger.log(`Application is running on: ${await app.getUrl()}`);

External Monitoring ​

Sentry Integration:

bash
pnpm add @sentry/node
typescript
// src/main.ts
import * as Sentry from '@sentry/node';

if (process.env.NODE_ENV === 'production') {
  Sentry.init({
    dsn: process.env.SENTRY_DSN,
  });
}

Performance Optimization ​

Application Optimization ​

  • Enable compression middleware
  • Implement caching strategies
  • Optimize database queries
  • Use connection pooling
  • Enable HTTP/2

Memory Management ​

Node.js Memory Configuration: The application includes memory management to prevent Out-of-Memory (OOM) crashes:

  1. Dockerfile Configuration: Sets NODE_OPTIONS="--max-old-space-size=8192" (suitable for high-memory containers; adjust per your container allocation)

    • Limits Node.js heap to the specified MB
    • Leave headroom for system overhead and native modules
  2. Memory Monitoring: Application logs memory usage:

    • On startup
    • Every 5 minutes in production (configurable)
    • Includes RSS, heap used/total, external memory, and array buffers
  3. Container Memory Recommendations:

    • 1GB: Minimum for basic applications
    • 2GB: Recommended for applications with image processing or S3 uploads
    • 4GB+: For high-traffic applications with complex operations

Adjusting Memory Limits: Update the NODE_OPTIONS environment variable in the Dockerfile or at runtime:

dockerfile
# For 2GB container (~75% of available memory)
ENV NODE_OPTIONS="--max-old-space-size=1536"

# For 4GB container
ENV NODE_OPTIONS="--max-old-space-size=3072"

Memory Optimization Best Practices:

  • Monitor memory usage logs to identify memory growth patterns
  • Review image processing code to ensure buffers are properly released
  • Use streaming for large file operations when possible
  • Implement connection pooling to limit database connection memory
  • Set appropriate Bull queue job retention policies

Troubleshooting OOM Issues:

  • Check application logs for memory usage patterns
  • Review container memory limits in your orchestration platform (ECS, Kubernetes, etc.)
  • Increase container memory allocation if consistently hitting limits
  • Investigate memory leaks using Node.js memory profiling tools

Infrastructure Optimization ​

  • Use CDN for static assets
  • Implement load balancing
  • Auto-scaling configuration
  • Database read replicas
  • Caching layers (Redis)

Backup and Recovery ​

Database Backup ​

Automated backup script:

bash
#!/bin/bash
# backup.sh

DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups"
DB_NAME="your_database"

# Create backup
pg_dump -h $DB_HOST -U $DB_USER -d $DB_NAME > $BACKUP_DIR/backup_$DATE.sql

# Compress backup
gzip $BACKUP_DIR/backup_$DATE.sql

# Remove old backups (keep last 7 days)
find $BACKUP_DIR -name "backup_*.sql.gz" -mtime +7 -delete

Cron job for automated backups:

bash
# Add to crontab
0 2 * * * /path/to/backup.sh

Application Backup ​

  • Regular code repository backups
  • Configuration file backups
  • SSL certificate backups
  • Log file archival

Scaling Strategies ​

Horizontal Scaling ​

  • Load balancer configuration
  • Multiple application instances
  • Database read replicas
  • Microservices architecture

Vertical Scaling ​

  • Increase server resources
  • Optimize application performance
  • Database performance tuning
  • Memory and CPU optimization

Auto-scaling Configuration ​

AWS Auto Scaling:

json
{
  "AutoScalingGroupName": "nest-boilerplate-asg",
  "MinSize": 1,
  "MaxSize": 10,
  "DesiredCapacity": 2,
  "TargetGroupARNs": ["arn:aws:elasticloadbalancing:..."],
  "HealthCheckType": "ELB",
  "HealthCheckGracePeriod": 300
}

Troubleshooting ​

Common Issues ​

Application won't start:

  • Check environment variables
  • Verify database connectivity
  • Check port availability
  • Review application logs

Database connection issues:

  • Verify database credentials
  • Check network connectivity
  • Confirm database server status
  • Review connection pool settings

Performance issues:

  • Monitor resource usage
  • Check database query performance
  • Review application logs
  • Analyze network latency

Debugging Tools ​

bash
# Check application status
pm2 status

# View real-time logs
pm2 logs --lines 100

# Monitor resource usage
htop
iostat
netstat -tulpn

# Database performance
EXPLAIN ANALYZE SELECT ...;

Log Analysis ​

bash
# Search for errors
grep -i error /var/log/app.log

# Monitor access patterns
tail -f /var/log/nginx/access.log

# Check system logs
journalctl -u your-service -f