Microservices Guide

    NestJS Microservices with PM2

    NestJS is the go-to framework for building scalable, maintainable Node.js microservices. Combined with PM2 for process management and comprehensive monitoring, you can create production-ready distributed systems on RamNode's reliable VPS hosting.

    Ubuntu 22.04/24.04
    PM2 + Prometheus + Grafana
    ⏱️ 45-60 minutes
    1

    Architecture Overview

    Our deployment architecture consists of multiple NestJS microservices communicating via various transport layers, managed by PM2 for process orchestration, with Prometheus and Grafana providing monitoring.

    ComponentPurpose
    NestJS ServicesAPI Gateway, User Service, Order Service, Notification Service
    PM2Process management, clustering, zero-downtime deployments
    RedisMessage broker for microservice communication
    PrometheusMetrics collection and time-series database
    GrafanaVisualization dashboards and alerting
    NGINXReverse proxy and load balancer
    2

    Prerequisites

    • Ubuntu 22.04 or 24.04 LTS with at least 4GB RAM and 2 vCPUs
    • Root or sudo access to the server
    • Domain name configured with DNS A records pointing to your server
    • Basic familiarity with Node.js and TypeScript
    3

    System Setup

    Update System and Install Dependencies

    Install build tools
    sudo apt update && sudo apt upgrade -y
    sudo apt install -y build-essential git curl wget software-properties-common

    Install Node.js via NVM

    Install NVM and Node.js 20
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
    source ~/.bashrc
    nvm install 20
    nvm use 20
    nvm alias default 20

    Install PM2 Globally

    Install PM2
    npm install -g pm2
    pm2 startup systemd

    Install and Configure Redis

    Install Redis
    sudo apt install -y redis-server
    sudo systemctl enable redis-server
    sudo systemctl start redis-server

    Configure Redis for production by editing /etc/redis/redis.conf:

    /etc/redis/redis.conf
    maxmemory 512mb
    maxmemory-policy allkeys-lru
    bind 127.0.0.1
    4

    Project Structure

    Create a monorepo structure for managing multiple microservices:

    Create project directory
    mkdir -p /var/www/microservices && cd /var/www/microservices
    npm init -y
    npm install -g @nestjs/cli

    Recommended directory structure:

    microservices/
    ├── apps/
    │   ├── api-gateway/
    │   ├── user-service/
    │   ├── order-service/
    │   └── notification-service/
    ├── libs/
    │   └── common/
    ├── ecosystem.config.js
    └── package.json
    5

    Create the API Gateway

    The API Gateway serves as the entry point for all client requests:

    Create API Gateway
    nest new api-gateway --directory apps/api-gateway --skip-git
    cd apps/api-gateway
    npm install @nestjs/microservices @nestjs/axios ioredis

    Create the main gateway module at apps/api-gateway/src/app.module.ts:

    app.module.ts
    import { Module } from '@nestjs/common';
    import { ClientsModule, Transport } from '@nestjs/microservices';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    
    @Module({
      imports: [
        ClientsModule.register([
          {
            name: 'USER_SERVICE',
            transport: Transport.REDIS,
            options: {
              host: 'localhost',
              port: 6379,
            },
          },
          {
            name: 'ORDER_SERVICE',
            transport: Transport.REDIS,
            options: {
              host: 'localhost',
              port: 6379,
            },
          },
        ]),
      ],
      controllers: [AppController],
      providers: [AppService],
    })
    export class AppModule {}
    6

    Create Microservices

    User Service

    Create User Service
    nest new user-service --directory apps/user-service --skip-git
    cd apps/user-service
    npm install @nestjs/microservices ioredis

    Configure the microservice bootstrap in apps/user-service/src/main.ts:

    main.ts
    import { NestFactory } from '@nestjs/core';
    import { MicroserviceOptions, Transport } from '@nestjs/microservices';
    import { AppModule } from './app.module';
    
    async function bootstrap() {
      const app = await NestFactory.createMicroservice<MicroserviceOptions>(
        AppModule,
        {
          transport: Transport.REDIS,
          options: {
            host: 'localhost',
            port: 6379,
          },
        },
      );
      await app.listen();
      console.log('User Service is running');
    }
    bootstrap();

    Create message patterns in apps/user-service/src/app.controller.ts:

    app.controller.ts
    import { Controller } from '@nestjs/common';
    import { MessagePattern, Payload } from '@nestjs/microservices';
    import { AppService } from './app.service';
    
    @Controller()
    export class AppController {
      constructor(private readonly appService: AppService) {}
    
      @MessagePattern({ cmd: 'get_user' })
      async getUser(@Payload() data: { userId: string }) {
        return this.appService.findUser(data.userId);
      }
    
      @MessagePattern({ cmd: 'create_user' })
      async createUser(@Payload() data: CreateUserDto) {
        return this.appService.createUser(data);
      }
    
      @MessagePattern({ cmd: 'validate_user' })
      async validateUser(@Payload() data: { email: string; password: string }) {
        return this.appService.validateUser(data.email, data.password);
      }
    }
    7

    PM2 Configuration

    Create the PM2 ecosystem configuration file at /var/www/microservices/ecosystem.config.js:

    ecosystem.config.js
    module.exports = {
      apps: [
        {
          name: 'api-gateway',
          script: 'dist/main.js',
          cwd: '/var/www/microservices/apps/api-gateway',
          instances: 'max',
          exec_mode: 'cluster',
          env: {
            NODE_ENV: 'production',
            PORT: 3000,
          },
          max_memory_restart: '500M',
          error_file: '/var/log/pm2/api-gateway-error.log',
          out_file: '/var/log/pm2/api-gateway-out.log',
          merge_logs: true,
          time: true,
        },
        {
          name: 'user-service',
          script: 'dist/main.js',
          cwd: '/var/www/microservices/apps/user-service',
          instances: 2,
          exec_mode: 'cluster',
          env: {
            NODE_ENV: 'production',
          },
          max_memory_restart: '300M',
        },
        {
          name: 'order-service',
          script: 'dist/main.js',
          cwd: '/var/www/microservices/apps/order-service',
          instances: 2,
          exec_mode: 'cluster',
          env: {
            NODE_ENV: 'production',
          },
          max_memory_restart: '300M',
        },
        {
          name: 'notification-service',
          script: 'dist/main.js',
          cwd: '/var/www/microservices/apps/notification-service',
          instances: 1,
          exec_mode: 'fork',
          env: {
            NODE_ENV: 'production',
          },
          max_memory_restart: '200M',
        },
      ],
    };

    PM2 Configuration Options

    OptionDescription
    instancesNumber of instances; use 'max' for CPU count
    exec_mode'cluster' for load balancing, 'fork' for single process
    max_memory_restartAutomatic restart when memory exceeds threshold
    merge_logsCombine logs from all cluster instances
    timePrefix log entries with timestamps

    Start Services

    Build and start services
    cd /var/www/microservices
    npm run build --workspaces
    sudo mkdir -p /var/log/pm2
    pm2 start ecosystem.config.js
    pm2 save

    Essential PM2 Commands

    CommandDescription
    pm2 statusView status of all processes
    pm2 logsStream logs from all services
    pm2 reload allZero-downtime reload
    pm2 monitReal-time monitoring dashboard
    8

    Prometheus Metrics

    Add Prometheus Metrics to NestJS

    Install dependencies
    npm install prom-client @willsoto/nestjs-prometheus

    Configure Prometheus module in your app.module.ts:

    app.module.ts
    import { Module } from '@nestjs/common';
    import { PrometheusModule, makeCounterProvider, makeHistogramProvider } from '@willsoto/nestjs-prometheus';
    
    @Module({
      imports: [
        PrometheusModule.register({
          path: '/metrics',
          defaultMetrics: {
            enabled: true,
          },
        }),
      ],
      providers: [
        makeCounterProvider({
          name: 'http_requests_total',
          help: 'Total number of HTTP requests',
          labelNames: ['method', 'path', 'status'],
        }),
        makeHistogramProvider({
          name: 'http_request_duration_seconds',
          help: 'HTTP request duration in seconds',
          labelNames: ['method', 'path', 'status'],
          buckets: [0.1, 0.5, 1, 2, 5],
        }),
      ],
    })
    export class AppModule {}

    Install Prometheus Server

    Install Prometheus
    sudo apt install -y prometheus

    Configure Prometheus at /etc/prometheus/prometheus.yml:

    prometheus.yml
    global:
      scrape_interval: 15s
      evaluation_interval: 15s
    
    scrape_configs:
      - job_name: 'prometheus'
        static_configs:
          - targets: ['localhost:9090']
      - job_name: 'api-gateway'
        static_configs:
          - targets: ['localhost:3000']
        metrics_path: /metrics
      - job_name: 'user-service'
        static_configs:
          - targets: ['localhost:3001']
        metrics_path: /metrics
      - job_name: 'pm2'
        static_configs:
          - targets: ['localhost:9209']
    Restart Prometheus
    sudo systemctl restart prometheus
    sudo systemctl enable prometheus
    9

    Grafana Dashboards

    Install Grafana

    Install Grafana
    sudo apt install -y apt-transport-https software-properties-common
    wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -
    echo "deb https://packages.grafana.com/oss/deb stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
    sudo apt update
    sudo apt install -y grafana
    sudo systemctl enable grafana-server
    sudo systemctl start grafana-server

    Access Grafana at http://your-server:3030 (default credentials: admin/admin).

    Essential Dashboard Queries

    Request Rate
    sum(rate(http_requests_total{job="api-gateway"}[5m])) by (path)
    Response Time P95
    histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{job="api-gateway"}[5m])) by (le, path))
    Error Rate
    sum(rate(http_requests_total{job="api-gateway", status=~"5.."}[5m])) / sum(rate(http_requests_total{job="api-gateway"}[5m])) * 100
    10

    NGINX Reverse Proxy

    Install NGINX
    sudo apt install -y nginx
    sudo systemctl enable nginx

    Create NGINX configuration at /etc/nginx/sites-available/microservices:

    NGINX configuration
    upstream api_gateway {
        least_conn;
        server 127.0.0.1:3000 weight=1 max_fails=3 fail_timeout=30s;
        keepalive 32;
    }
    
    server {
        listen 80;
        server_name api.yourdomain.com;
    
        location / {
            proxy_pass http://api_gateway;
            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;
        }
    
        location /metrics {
            deny all;
            return 404;
        }
    }
    Enable configuration
    sudo ln -s /etc/nginx/sites-available/microservices /etc/nginx/sites-enabled/
    sudo nginx -t
    sudo systemctl reload nginx
    11

    Health Checks and Alerting

    NestJS Health Check Module

    Install Terminus
    npm install @nestjs/terminus
    health.controller.ts
    import { Controller, Get } from '@nestjs/common';
    import { HealthCheck, HealthCheckService, MemoryHealthIndicator, DiskHealthIndicator } from '@nestjs/terminus';
    
    @Controller('health')
    export class HealthController {
      constructor(
        private health: HealthCheckService,
        private memory: MemoryHealthIndicator,
        private disk: DiskHealthIndicator,
      ) {}
    
      @Get()
      @HealthCheck()
      check() {
        return this.health.check([
          () => this.memory.checkHeap('memory_heap', 200 * 1024 * 1024),
          () => this.memory.checkRSS('memory_rss', 300 * 1024 * 1024),
          () => this.disk.checkStorage('disk', { path: '/', thresholdPercent: 0.9 }),
        ]);
      }
    }

    Prometheus Alerting Rules

    /etc/prometheus/alerts.yml
    groups:
      - name: microservices
        rules:
          - alert: ServiceDown
            expr: up == 0
            for: 1m
            labels:
              severity: critical
            annotations:
              summary: "Service {{ $labels.job }} is down"
          - alert: HighErrorRate
            expr: sum(rate(http_requests_total{status=~"5.."}[5m])) by (job) /
                  sum(rate(http_requests_total[5m])) by (job) > 0.05
            for: 5m
            labels:
              severity: warning
          - alert: PM2ProcessRestarting
            expr: increase(pm2_restarts[5m]) > 3
            for: 1m
            labels:
              severity: warning
    12

    Zero-Downtime Deployments

    Create a deployment script at /var/www/microservices/deploy.sh:

    deploy.sh
    #!/bin/bash
    set -e
    SERVICE=$1
    
    if [ -z "$SERVICE" ]; then
      echo "Usage: ./deploy.sh <service-name|all>"
      exit 1
    fi
    
    cd /var/www/microservices
    echo "Pulling latest changes..."
    git pull origin main
    
    if [ "$SERVICE" = "all" ]; then
      echo "Building all services..."
      npm run build --workspaces
      echo "Performing zero-downtime reload..."
      pm2 reload ecosystem.config.js
    else
      echo "Building $SERVICE..."
      npm run build --workspace=apps/$SERVICE
      echo "Performing zero-downtime reload for $SERVICE..."
      pm2 reload $SERVICE
    fi
    
    pm2 save
    echo "Deployment complete!"
    pm2 status
    Make executable
    chmod +x /var/www/microservices/deploy.sh

    Graceful Shutdown Configuration

    Add graceful shutdown handling in each NestJS service:

    main.ts
    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    
    async function bootstrap() {
      const app = await NestFactory.create(AppModule);
      app.enableShutdownHooks();
      
      process.on('SIGTERM', async () => {
        console.log('SIGTERM received, gracefully shutting down...');
        await app.close();
        process.exit(0);
      });
      
      await app.listen(process.env.PORT || 3000);
    }
    bootstrap();
    13

    Troubleshooting

    IssueSolution
    Service not startingCheck logs with pm2 logs <service>; verify build completed
    High memory usageAdjust max_memory_restart; check for memory leaks with pm2 monit
    Redis connection failedVerify Redis is running: systemctl status redis-server
    Metrics not appearingEnsure /metrics endpoint is accessible; check Prometheus targets
    Cluster mode issuesUse fork mode for stateful services; ensure shared session storage
    502 Bad GatewayCheck if service is running; verify NGINX upstream configuration

    Log Rotation

    Configure PM2 log rotation
    pm2 install pm2-logrotate
    pm2 set pm2-logrotate:max_size 50M
    pm2 set pm2-logrotate:retain 10
    pm2 set pm2-logrotate:compress true