Skip to content

Backend Deployment — EC2 + PM2 + nginx

The backend runs as a Node.js cluster managed by PM2 behind an nginx reverse proxy with Certbot SSL on an Ubuntu EC2 instance.


Server Details

SettingValue
Monorepo path/home/ubuntu/my-app
PM2 process nameprod-logistech
Backend port3500
Domainapp.intecoglogistech.com
OSUbuntu 22.04

PM2 Configuration

File: ecosystem.config.cjs (monorepo root)

js
module.exports = {
  apps: [{
    name: 'prod-logistech',
    script: './apps/backend/dist/app.js',
    cwd: '/home/ubuntu/my-app',
    instances: 'max',       // one worker per CPU core
    exec_mode: 'cluster',   // Node.js cluster mode for load balancing
    env: {
      NODE_ENV: 'production',
      PORT: 3500
    }
  }]
}

instances: 'max' automatically starts one PM2 worker per available CPU core.


Deploy Scripts

Both deploy scripts are defined in the root package.json:

bash
# Build + pm2 reload (zero-downtime, use for all regular deploys)
pnpm deploy:backend

# Build + pm2 start (use on first deploy or when ecosystem.config.cjs changes)
pnpm deploy:backend:fresh

Internally:

bash
# deploy:backend
pnpm --filter @my-app/shared build \
  && pnpm --filter @my-app/backend build \
  && pm2 reload ecosystem.config.cjs --update-env

# deploy:backend:fresh
pnpm --filter @my-app/shared build \
  && pnpm --filter @my-app/backend build \
  && pm2 start ecosystem.config.cjs

When to use each

CommandWhen to use
pnpm deploy:backendNormal code updates (zero-downtime cluster reload)
pnpm deploy:backend:freshFirst deploy, ecosystem config changed, full restart

nginx Configuration

Reference file: nginx.conf (monorepo root — deploy to /etc/nginx/sites-available/)

nginx
server {
    server_name app.intecoglogistech.com;

    location / {
        proxy_pass          http://127.0.0.1:3500;
        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_cache_bypass  $http_upgrade;
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate     /etc/letsencrypt/live/app.intecoglogistech.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.intecoglogistech.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
}

server {
    if ($host = app.intecoglogistech.com) {
        return 301 https://$host$request_uri;
    }
    listen 80;
    server_name app.intecoglogistech.com;
    return 404;
}

SSL is managed by Certbot (Let's Encrypt). Certbot auto-renews certificates.


Useful PM2 Commands

bash
# Check process status
pm2 status

# View live logs
pm2 logs prod-logistech

# Reload with zero downtime
pm2 reload prod-logistech

# Stop
pm2 stop prod-logistech

# Delete process from PM2 registry
pm2 delete prod-logistech

# Save current process list for auto-restart on reboot
pm2 save
pm2 startup

First-Time Server Setup

  1. Install Node.js 18+, pnpm, PM2, nginx, Certbot
  2. Clone the repository to /home/ubuntu/my-app
  3. Create apps/backend/.env with all required env vars
  4. Run:
    bash
    pnpm install
    pnpm deploy:backend:fresh
  5. Configure nginx and obtain SSL cert:
    bash
    sudo certbot --nginx -d app.intecoglogistech.com

See docs/server-setup-ec2.md for the full step-by-step EC2 setup guide.

Intecog Logistech IoT Monitoring Platform