> ## Documentation Index
> Fetch the complete documentation index at: https://docs.loremstock.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Preparing a Production Deployment Environment for Node.js

> Set up a Linux VPS for Node.js with NVM, PM2 process manager, Nginx reverse proxy, SSL/TLS via Certbot, and ufw firewall configuration for production deployment.

Preparing a deployment environment is the process of configuring a server so it can run your Node.js application reliably, securely, and continuously. This page covers everything from choosing a deployment target to configuring PM2, Nginx, a firewall, and SSL certificates.

## Development vs Staging vs Production

| Environment     | Purpose                   | Who Uses It    |
| --------------- | ------------------------- | -------------- |
| **Development** | Local coding and testing  | Developer only |
| **Staging**     | Pre-production validation | Team + QA      |
| **Production**  | Serving real users        | End users      |

Never test on production. Always stage before deploying.

## Deployment Targets

| Option         | Examples                                | Best For                     |
| -------------- | --------------------------------------- | ---------------------------- |
| **VPS**        | Ubuntu on DigitalOcean, Linode, Hetzner | Full control, cost-effective |
| **PaaS**       | Heroku, Railway, Render                 | Simpler ops, less control    |
| **Containers** | Docker on any VPS or Kubernetes         | Portability, scaling         |

This page covers VPS deployment on Ubuntu.

## Server Setup: Ubuntu 22.04 LTS

### Step 1: Update the Server

```bash theme={null}
ssh ubuntu@your-server-ip

sudo apt update && sudo apt upgrade -y
sudo apt install -y git curl build-essential
```

### Step 2: Install Node.js via NVM

```bash theme={null}
# Install NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

# Reload shell config
source ~/.bashrc

# Install Node.js LTS
nvm install --lts
nvm use --lts

# Verify
node --version
npm --version
```

<Note>
  Always use NVM on servers, not `apt install nodejs`. NVM lets you switch Node.js versions without reinstalling and avoids permission issues with global npm packages.
</Note>

### Step 3: Install PM2

```bash theme={null}
npm install -g pm2
pm2 --version
```

### Step 4: Install Nginx

```bash theme={null}
sudo apt install nginx -y
sudo systemctl start nginx
sudo systemctl enable nginx  # Start on boot
sudo systemctl status nginx
```

## PM2: Process Manager

PM2 keeps your Node.js app alive, restarts it on crashes, and provides monitoring. This is the standard for production Node.js.

### Why Not Just `node app.js`?

| Feature                | node app.js | PM2                    |
| ---------------------- | ----------- | ---------------------- |
| Auto-restart on crash  | No          | Yes                    |
| Survives server reboot | No          | Yes (with pm2 startup) |
| Log management         | None        | Built-in               |
| Cluster mode           | Manual      | One flag               |
| Monitoring             | None        | pm2 monit              |

### PM2 Commands Reference

| Command                         | What It Does               |
| ------------------------------- | -------------------------- |
| `pm2 start app.js`              | Start application          |
| `pm2 start app.js --name myapp` | Start with custom name     |
| `pm2 list`                      | List all running processes |
| `pm2 logs myapp`                | Tail logs                  |
| `pm2 restart myapp`             | Restart with downtime      |
| `pm2 reload myapp`              | Zero-downtime reload       |
| `pm2 stop myapp`                | Stop the process           |
| `pm2 delete myapp`              | Remove from PM2            |
| `pm2 startup`                   | Generate startup command   |
| `pm2 save`                      | Save current process list  |
| `pm2 monit`                     | Live dashboard             |

### ecosystem.config.js

```javascript theme={null}
// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'swdbd401-api',
    script: 'app.js',
    instances: 1,               // Use 'max' for cluster mode
    exec_mode: 'fork',          // Use 'cluster' for multiple instances
    watch: false,               // Don't watch files in production
    max_memory_restart: '500M', // Restart if memory exceeds 500MB

    env: {
      NODE_ENV: 'development',
      PORT: 3000
    },
    env_production: {
      NODE_ENV: 'production',
      PORT: 3000
    },

    error_file: './logs/err.log',
    out_file: './logs/out.log',
    log_date_format: 'YYYY-MM-DD HH:mm:ss Z',

    // Auto-restart if app exits
    autorestart: true,
    restart_delay: 5000         // Wait 5 seconds before restarting
  }]
};
```

Start with ecosystem file:

```bash theme={null}
pm2 start ecosystem.config.js --env production
pm2 save
```

### Make PM2 Survive Reboots

```bash theme={null}
pm2 startup     # PM2 outputs a command to run - copy and run it!
# Example output: sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu
# Run that command, then:
pm2 save        # Saves current process list to resurrect on boot
```

## Nginx: Reverse Proxy

Nginx sits in front of Node.js, handling SSL, static files, and load balancing.

```text theme={null}
Browser -> HTTPS:443 -> Nginx -> HTTP:3000 -> Node.js
```

### Configure Nginx

```nginx theme={null}
# /etc/nginx/sites-available/myapp
server {
    listen 80;
    server_name api.yourdomain.com;

    # Redirect HTTP to HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    server_name api.yourdomain.com;

    # SSL certificates (configured by Certbot)
    ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem;

    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_cache_bypass $http_upgrade;
    }
}
```

Enable and test:

```bash theme={null}
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t        # Test configuration
sudo systemctl reload nginx
```

## Firewall with ufw

```bash theme={null}
# Allow SSH (CRITICAL: do this before enabling firewall!)
sudo ufw allow 22

# Allow web traffic
sudo ufw allow 80
sudo ufw allow 443

# Enable firewall
sudo ufw enable

# Check status
sudo ufw status
```

<Warning>
  Always run `ufw allow 22` BEFORE `ufw enable`. If you lock yourself out of SSH, you will need to use the server provider's emergency console to recover access.
</Warning>

## SSL Certificate with Certbot

```bash theme={null}
sudo apt install certbot python3-certbot-nginx -y

# Get certificate and configure Nginx automatically
sudo certbot --nginx -d api.yourdomain.com

# Test auto-renewal
sudo certbot renew --dry-run
```

Certbot auto-renews certificates via a cron job. Let's Encrypt certificates are free and valid for 90 days.

## NODE\_ENV=production Effects

When `NODE_ENV=production`:

* Express disables detailed error messages
* Express enables view caching and performance optimizations
* Many libraries disable development-only features
* Your config module should load production values

## Pre-Deployment Checklist

<Steps>
  <Step title="Environment variables">
    Verify all required .env variables are set on the server
  </Step>

  <Step title="Database connection">
    Confirm DB\_URI points to production database and connection works
  </Step>

  <Step title="Firewall rules">
    Only ports 22, 80, and 443 open
  </Step>

  <Step title="SSL certificate">
    HTTPS working, HTTP redirecting to HTTPS
  </Step>

  <Step title="PM2 startup">
    PM2 configured to restart on server reboot
  </Step>

  <Step title="Health check">
    GET /health returns 200
  </Step>

  <Step title="npm audit">
    No high or critical vulnerabilities
  </Step>
</Steps>

## Key Terms

| Term              | Definition                                                                                |
| ----------------- | ----------------------------------------------------------------------------------------- |
| **VPS**           | Virtual Private Server. A virtual machine rented from a cloud provider.                   |
| **PaaS**          | Platform as a Service. Managed hosting where you deploy code without managing servers.    |
| **Reverse proxy** | Server that sits in front of the application, forwarding requests. Nginx in this context. |
| **PM2**           | Production process manager for Node.js. Keeps apps running and restarts on crash.         |
| **SSL/TLS**       | Secure Sockets Layer / Transport Layer Security. Protocol for HTTPS.                      |
| **Certbot**       | Tool that obtains free SSL certificates from Let's Encrypt.                               |
| **ufw**           | Uncomplicated Firewall. Ubuntu's user-friendly interface to iptables.                     |
