Voltar para Artigos
DevOps★ Destaque11 min de leitura

Deploy Master: Node.js com PM2 e NGINX

Do zero ao deploy profissional: PM2 em modo cluster aproveitando todos os núcleos, ecosystem.config.js tipado, zero-downtime reload, NGINX como reverse proxy com rate limiting, GZIP e upload de arquivos.

12 de agosto de 2026

Rodar node server.js em produção é uma das práticas mais arriscadas do ecossistema Node.js. Uma exceção não tratada derruba o processo. O servidor reinicia após uma atualização de segurança. Você tem 4 núcleos de CPU e só usa um. Sem nada para reiniciar automaticamente, sua API fica offline até alguém notar e conectar no servidor.

PM2 resolve a questão do processo (reinício automático, clustering, logs, monitoramento). NGINX resolve a questão do tráfego (SSL termination, rate limiting, servir arquivos estáticos, múltiplos apps na mesma porta 443). Juntos, eles formam a base de produção mais utilizada para Node.js em VPS.

PM2: Gerenciador de Processos

bash
npm install -g pm2

# Verifica a instalação
pm2 --version

ecosystem.config.js: Configuração Declarativa

Sempre use o arquivo de configuração ecosystem.config.js ao invés de flags de linha de comando. É versionável, legível e suporta múltiplos apps e ambientes:

ecosystem.config.js
module.exports = {
  apps: [
    {
      name: 'api',
      script: 'dist/server.js',  // Arquivo compilado

      // Modo cluster: usa todos os núcleos da CPU disponíveis
      // -1 = número de CPUs - 1 (deixa um núcleo para o SO)
      // 'max' = todos os núcleos
      instances: 'max',
      exec_mode: 'cluster',

      // Reinicia se a memória ultrapassar 512MB
      // Evita memory leaks derrubarem o servidor
      max_memory_restart: '512M',

      // Reinicia automaticamente se o processo cair
      autorestart: true,
      watch: false, // Nunca em produção: recarrega a cada mudança de arquivo

      // Variáveis de ambiente por ambiente
      env: {
        NODE_ENV: 'development',
        PORT: 3333,
      },
      env_production: {
        NODE_ENV: 'production',
        PORT: 3333,
      },

      // Configuração de logs
      log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
      error_file: '/var/log/pm2/api-error.log',
      out_file: '/var/log/pm2/api-out.log',
      merge_logs: true, // Unifica logs de todas as instâncias do cluster

      // Exponential backoff no restart: evita restart loop
      // Se o app fica caindo muito rapidamente, o PM2 aumenta o intervalo
      restart_delay: 1000,
      max_restarts: 10,
      min_uptime: '10s',
    },
  ],
};
bash
# Compila o TypeScript antes de fazer deploy
npm run build

# Inicia em modo produção
pm2 start ecosystem.config.js --env production

# Status de todos os processos
pm2 status

# Logs em tempo real
pm2 logs api
pm2 logs api --lines 100  # Últimas 100 linhas

# Monitoramento interativo (CPU, memória, logs)
pm2 monit

Zero-Downtime Deploy com pm2 reload

Em modo cluster, pm2 reload atualiza as instâncias uma por vez — enquanto uma instância está sendo reiniciada, as outras continuam servindo tráfego. Isso garante zero downtime durante deploys:

bash
# Script de deploy típico (rodado via CI/CD ou manualmente)
cd /var/www/minha-api

# 1. Busca as mudanças
git pull origin main

# 2. Instala novas dependências (se houver)
npm ci --only=production

# 3. Compila o TypeScript
npm run build

# 4. Roda migrations (cuidado: locks de tabela em produção)
npm run migration:run

# 5. ZERO DOWNTIME: reinicia instâncias uma a uma
pm2 reload api

# Diferença entre reload e restart:
# pm2 restart api → mata TODOS os processos e reinicia (downtime)
# pm2 reload api  → reinicia UM POR UM no cluster (zero downtime)

Autostart: PM2 na Inicialização do Sistema

bash
# Gera e configura o script de inicialização para o sistema operacional
# Execute o comando que o pm2 startup imprimir
pm2 startup
# → sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu

# Salva a lista atual de processos para persistir após reinicialização
pm2 save

# Verifica se o serviço PM2 está ativo no systemd
sudo systemctl status pm2-ubuntu

NGINX: Reverse Proxy Completo

NGINX escuta a porta 443 (HTTPS, após Certbot configurar) e repassa o tráfego para o Node.js na porta interna. Além do proxy, ele lida com rate limiting, GZIP, uploads e servir arquivos estáticos diretamente (muito mais eficiente que o Node.js):

api.seuapp.com
# Rate limiting: define zonas de limitação
# 10MB de memória para armazenar estados dos IPs
# 10 requisições por segundo por IP
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=auth_limit:10m rate=2r/s;

server {
    listen 80;
    server_name api.seuapp.com;
    # Certbot adicionará o redirecionamento para HTTPS aqui

    # Limite de tamanho do body (padrão 1MB — aumente para uploads)
    client_max_body_size 10M;

    # Timeouts para requests longas (ex: processar vídeo, gerar relatório)
    proxy_connect_timeout 60s;
    proxy_read_timeout    120s;
    proxy_send_timeout    120s;

    # GZIP: comprime respostas maiores que 1KB
    gzip on;
    gzip_min_length 1024;
    gzip_types
        application/json
        application/javascript
        text/css
        text/plain;
    gzip_vary on;

    # Rate limiting para rotas de autenticação (mais restrito)
    location ~ ^/api/auth/ {
        limit_req zone=auth_limit burst=5 nodelay;
        limit_req_status 429;

        proxy_pass http://localhost:3333;
        proxy_http_version 1.1;
        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;
    }

    # Rotas gerais da API com rate limiting padrão
    location /api/ {
        limit_req zone=api_limit burst=20 nodelay;
        limit_req_status 429;

        proxy_pass http://localhost:3333;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;    # WebSocket
        proxy_set_header Connection 'upgrade';      # WebSocket
        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;
    }

    # Servir arquivos estáticos diretamente pelo NGINX
    # Muito mais eficiente que passar pelo Node.js
    location /uploads/ {
        alias /var/www/minha-api/uploads/;
        expires 7d;           # Cache no browser por 7 dias
        add_header Cache-Control "public, immutable";

        # Previne execução de scripts em uploads
        location ~* \.(php|py|sh|pl)$ {
            return 403;
        }
    }
}
bash
# Ativa o site
sudo ln -s /etc/nginx/sites-available/api.seuapp.com \
           /etc/nginx/sites-enabled/

# Testa a configuração (NUNCA pule este passo!)
sudo nginx -t

# Aplica a configuração sem downtime
sudo systemctl reload nginx

# Verifica erros em tempo real
sudo tail -f /var/log/nginx/error.log

Monitoramento e Alertas

bash
# PM2 Plus (cloud) ou configure logs para seu sistema de observabilidade
# Para alertas básicos, crie um script de health check:

# /usr/local/bin/check-api-health.sh
#!/bin/bash
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3333/health)

if [ "$HTTP_STATUS" != "200" ]; then
  echo "API unhealthy: status $HTTP_STATUS" | mail -s "ALERTA: API fora do ar" seu@email.com
  pm2 reload api  # Tenta recarregar
fi

# Adicione ao crontab para verificar a cada minuto:
# * * * * * /usr/local/bin/check-api-health.sh

Sempre implemente uma rota /health na sua API que verifica a saúde das dependências (banco de dados, Redis, serviços externos) e retorna HTTP 200 quando tudo está OK. O PM2, o NGINX, load balancers e ferramentas de monitoramento usam esse endpoint para determinar se a instância está saudável.

Conclusão

PM2 + NGINX cobre todos os requisitos de produção para Node.js em VPS: resiliência (autorestart), performance (clustering de CPU, GZIP), segurança (rate limiting, SSL via Certbot), e eficiência (arquivos estáticos servidos pelo NGINX). O pm2 reload com zero downtime garante que deploys não interrompam usuários. Para escalonamento além de uma única VPS, o próximo passo é Kubernetes ou serviços gerenciados como AWS ECS.