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.
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
npm install -g pm2
# Verifica a instalação
pm2 --versionecosystem.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:
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',
},
],
};# 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 monitZero-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:
# 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
# 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-ubuntuNGINX: 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):
# 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;
}
}
}# 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.logMonitoramento e Alertas
# 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.shSempre 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.