Gerenciamento de processos PM2 para Node.js em produção
Quando seu aplicativo Node.js trava às 2h, PM2 é a diferença entre ele reiniciar automaticamente e seus usuários verem uma página em branco até você acordar. PM2 é um gerenciador de processos testado em batalha que lida com reinicializações automáticas, clustering para utilização de vários núcleos, agregação de logs e implantações com tempo de inatividade zero - tudo com um único arquivo de configuração que reside em seu repositório.
Este guia cobre uma configuração de produção PM2 gerenciando 5 processos Node.js simultaneamente: Next.js (frontend), NestJS (API), Docusaurus (docs) e dois sites de marca. Os padrões se aplicam igualmente a implantações de processo único.
Principais conclusões
- O arquivo
ecosystem.config.cjs(CommonJS, não.js) funciona com projetos ESModule e CommonJS- O sinalizador
--update-envé necessário ao reiniciar para obter novas variáveis de ambiente- Nunca use
pm2 restart allsem--update-envapós atualizar.env.localwatch: falseem produção — a observação de arquivos causa loops de reinicialização infinitos com saídas de compilaçãomax_memory_restartfornece proteção automática contra vazamento de memória sem interromper o processo permanentementenode_args: '--max-old-space-size=4096'evita falhas de OOM em operações com uso intensivo de memória- Os logs PM2 giram com o módulo
pm2-logrotate— instale-o imediatamente após o próprio PM2pm2 saveepm2 startuppersistem sua lista de processos durante as reinicializações do servidor
Instalação
# Install PM2 globally
npm install -g pm2
# Install the log rotation module immediately
pm2 install pm2-logrotate
# Configure log rotation
pm2 set pm2-logrotate:max_size 50M
pm2 set pm2-logrotate:retain 7
pm2 set pm2-logrotate:compress true
pm2 set pm2-logrotate:dateFormat YYYY-MM-DD
Arquivo de configuração do ecossistema
O arquivo ecosystem.config.cjs (formato CommonJS para trabalhar com projetos ESM e CJS) define todos os seus processos:
// ecosystem.config.cjs
module.exports = {
apps: [
// ─── Next.js Frontend ────────────────────────────────────────────
{
name: 'ecosire-web',
script: 'node_modules/.bin/next',
args: 'start',
cwd: '/opt/ecosire/app/apps/web',
instances: 1, // Single instance — Next.js handles its own multi-threading
exec_mode: 'fork',
env: {
NODE_ENV: 'production',
PORT: 3000,
},
// Memory management
max_memory_restart: '1G',
node_args: '--max-old-space-size=1024',
// Logging
out_file: '/var/log/pm2/ecosire-web.out.log',
error_file: '/var/log/pm2/ecosire-web.err.log',
merge_logs: true,
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
// Restart behavior
watch: false,
restart_delay: 3000,
max_restarts: 10,
min_uptime: '30s', // Must stay up 30s to count as successful start
autorestart: true,
// Graceful shutdown
kill_timeout: 30000, // 30 seconds to shut down gracefully
wait_ready: true, // Wait for process.send('ready')
listen_timeout: 60000,
},
// ─── NestJS API ──────────────────────────────────────────────────
{
name: 'ecosire-api',
script: 'dist/main.js',
cwd: '/opt/ecosire/app/apps/api',
instances: 2, // Cluster mode for multi-core utilization
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3001,
},
max_memory_restart: '512M',
node_args: '--max-old-space-size=512',
out_file: '/var/log/pm2/ecosire-api.out.log',
error_file: '/var/log/pm2/ecosire-api.err.log',
merge_logs: true,
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
watch: false,
restart_delay: 2000,
max_restarts: 10,
min_uptime: '20s',
autorestart: true,
kill_timeout: 15000,
// Graceful cluster reload support
listen_timeout: 30000,
},
// ─── Docusaurus Docs ─────────────────────────────────────────────
{
name: 'ecosire-docs',
script: 'node_modules/.bin/docusaurus',
args: 'serve',
cwd: '/opt/ecosire/app/apps/docs',
instances: 1,
exec_mode: 'fork',
env: {
NODE_ENV: 'production',
PORT: 3002,
},
max_memory_restart: '256M',
out_file: '/var/log/pm2/ecosire-docs.out.log',
error_file: '/var/log/pm2/ecosire-docs.err.log',
merge_logs: true,
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
watch: false,
restart_delay: 3000,
max_restarts: 5,
min_uptime: '30s',
autorestart: true,
kill_timeout: 10000,
},
// ─── Brand Site: Odovation ───────────────────────────────────────
{
name: 'odovation-web',
script: 'node_modules/.bin/next',
args: 'start',
cwd: '/opt/ecosire/app/apps/odovation',
instances: 1,
exec_mode: 'fork',
env: {
NODE_ENV: 'production',
PORT: 3010,
},
max_memory_restart: '512M',
out_file: '/var/log/pm2/odovation-web.out.log',
error_file: '/var/log/pm2/odovation-web.err.log',
merge_logs: true,
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
watch: false,
restart_delay: 3000,
max_restarts: 10,
min_uptime: '30s',
autorestart: true,
},
// ─── Brand Site: MuhammadAmir ────────────────────────────────────
{
name: 'muhammadamir-web',
script: 'node_modules/.bin/next',
args: 'start',
cwd: '/opt/ecosire/app/apps/muhammadamir',
instances: 1,
exec_mode: 'fork',
env: {
NODE_ENV: 'production',
PORT: 3020,
},
max_memory_restart: '512M',
out_file: '/var/log/pm2/muhammadamir-web.out.log',
error_file: '/var/log/pm2/muhammadamir-web.err.log',
merge_logs: true,
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
watch: false,
restart_delay: 3000,
max_restarts: 10,
min_uptime: '30s',
autorestart: true,
},
],
};
Comandos principais do PM2
# Start all processes from ecosystem file
pm2 start ecosystem.config.cjs
# Restart all (with updated environment variables)
pm2 restart ecosystem.config.cjs --update-env
# Graceful reload (zero-downtime for cluster mode)
pm2 reload ecosystem.config.cjs
# Stop all processes
pm2 stop all
# Delete all processes from PM2 registry
pm2 delete all
# Individual process management
pm2 restart ecosire-api
pm2 stop ecosire-docs
pm2 logs ecosire-web --lines 100
# Real-time monitoring dashboard
pm2 monit
# Status overview
pm2 status
pm2 list
Inicialização na reinicialização do servidor
Sem configuração de inicialização, todos os processos PM2 serão perdidos na reinicialização do servidor:
# Generate and install the startup script for your init system
pm2 startup
# Copy the output command and run it (it looks like:)
# sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u ubuntu --hp /home/ubuntu
# Save the current process list
pm2 save
# This creates ~/.pm2/dump.pm2 — processes are restored on reboot
# Verify startup works
pm2 resurrect # Manually restore from dump.pm2
Cada vez que você adicionar ou remover processos, execute pm2 save novamente para atualizar o arquivo de despejo.
Implantações com tempo de inatividade zero
Para NestJS no modo cluster, o PM2 oferece suporte a recargas com tempo de inatividade zero:
# Reload restarts workers one at a time (zero-downtime)
# Old workers handle requests while new ones start
pm2 reload ecosire-api
# vs restart — kills all workers simultaneously (brief downtime)
pm2 restart ecosire-api
Para Next.js (que é executado no modo fork, instância única), o tempo de inatividade zero requer uma abordagem diferente. Use a configuração wait_ready + listen_timeout com um sinal de inicialização do seu aplicativo:
// apps/web — this is handled automatically by Next.js
// But for NestJS, send the ready signal explicitly:
// apps/api/src/main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3001);
// Signal PM2 that the process is ready
if (process.send) {
process.send('ready');
}
}
bootstrap();
Gerenciamento de registros
Os logs PM2 podem encher seu disco se não forem gerenciados. Configure a rotação de log imediatamente:
# Install log rotation module
pm2 install pm2-logrotate
# Configuration
pm2 set pm2-logrotate:max_size 50M # Rotate when log reaches 50MB
pm2 set pm2-logrotate:retain 7 # Keep 7 days of logs
pm2 set pm2-logrotate:compress true # Gzip rotated logs
pm2 set pm2-logrotate:dateFormat YYYY-MM-DD
pm2 set pm2-logrotate:workerInterval 30 # Check rotation interval (seconds)
pm2 set pm2-logrotate:rotateInterval '0 0 * * *' # Daily at midnight
Comandos de registro úteis:
# View all logs combined
pm2 logs
# View specific process logs
pm2 logs ecosire-api
# View with timestamps
pm2 logs --timestamp
# Flush all log files
pm2 flush
# Tail error logs only
pm2 logs ecosire-api --err --lines 200
Monitoramento e Métricas
PM2 Plus (anteriormente Keymetrics) fornece monitoramento baseado em nuvem. Para monitoramento auto-hospedado:
# Built-in terminal dashboard
pm2 monit
# Get JSON status for scripting/monitoring integration
pm2 jlist # JSON process list
pm2 prettylist # Formatted process list
# Integrate with your monitoring stack
pm2 set pm2-server-monit:interval 5 # Metrics collection interval
Para monitoramento de produção, exponha as métricas PM2 ao Prometheus:
npm install -g pm2-prometheus-exporter
pm2 set pm2-prometheus-exporter:port 9209
# Scrape in Prometheus config:
# - job_name: pm2
# static_configs:
# - targets: ['localhost:9209']
Integração de script de implantação
Uma sequência de implantação típica:
#!/bin/bash
# scripts/deploy-production.sh
set -e
echo "=== Starting deployment ==="
# 1. Pull latest code
git pull origin main
# 2. Install dependencies
pnpm install --frozen-lockfile
# 3. Build all apps (with Turbo remote cache)
TURBO_TOKEN="$TURBO_TOKEN" TURBO_TEAM="$TURBO_TEAM" \
npx turbo run build
# 4. Run database migrations
pnpm --filter @ecosire/db db:migrate
# 5. Restart PM2 processes
# --update-env picks up changes in .env.local
pm2 restart ecosystem.config.cjs --update-env
# 6. Wait for processes to stabilize
sleep 10
# 7. Health checks
curl -f https://ecosire.com/ -o /dev/null -s || {
echo "Web health check failed — rolling back"
git revert HEAD --no-edit
pm2 restart ecosystem.config.cjs --update-env
exit 1
}
curl -f https://api.ecosire.com/api/health -o /dev/null -s || {
echo "API health check failed — rolling back"
git revert HEAD --no-edit
pm2 restart ecosystem.config.cjs --update-env
exit 1
}
# 8. Save process state
pm2 save
echo "=== Deployment complete ==="
Armadilhas e soluções comuns
** Armadilha 1: Esquecer --update-env**
Após atualizar .env.local, executar pm2 restart all sem --update-env faz com que os processos sejam reiniciados com as variáveis de ambiente antigas. Sempre use pm2 restart ecosystem.config.cjs --update-env.
Armadilha 2: usar watch: true na produção
watch: true reinicia o processo quando qualquer arquivo é alterado. Na produção, os resultados do build mudam a cada implantação — isso causa loops de reinicialização infinitos. Sempre defina watch: false.
Armadilha 3: Não lidar com o SIGTERM para um desligamento normal
PM2 envia SIGTERM ao reiniciar/parar. Se o seu aplicativo não lidar com isso, o PM2 espera kill_timeout milissegundos e envia SIGKILL - o que pode causar perda de solicitações. Lidar com SIGTERM no NestJS:
// main.ts
const app = await NestFactory.create(AppModule);
await app.listen(3001);
// Graceful shutdown
process.on('SIGTERM', async () => {
await app.close();
process.exit(0);
});
Armadilha 4: Ficar sem espaço em disco de log do PM2
Sem pm2-logrotate, os logs PM2 crescem indefinidamente. Uma API com tráfego intenso pode gerar gigabytes de logs por dia. Instale pm2-logrotate imediatamente e defina max_size (50 MB) e retain (7 dias) razoáveis.
Armadilha 5: Perda de processos após a reinicialização
pm2 start não persiste processos durante reinicializações. Sempre execute pm2 startup + pm2 save após a configuração inicial. Se os processos desaparecerem após uma reinicialização, execute pm2 resurrect para restaurar a partir do dump salvo.
Perguntas frequentes
Quando devo usar o modo cluster versus modo bifurcação?
Use o modo cluster para cargas de trabalho vinculadas à CPU (APIs NestJS com computação pesada e processamento de dados). O modo cluster gera processos de trabalho instances e balanceamento de carga PM2 entre eles – aproveitando todos os núcleos da CPU. Use o modo fork para cargas de trabalho vinculadas a E/S (Next.js, serviço de arquivo estático) ou quando o processo não oferece suporte a clustering (scripts de thread único, serviço Docusaurus). Next.js lida com seus próprios threads de trabalho internamente, portanto, o modo fork com instances: 1 está correto.
Como executo o PM2 em um contêiner Docker?
PM2 no Docker usa pm2-docker (ou pm2-runtime) em vez de pm2 para manipular os sinais corretamente. A versão de tempo de execução não daemoniza (o que causaria a saída do Docker), encaminha sinais adequadamente para processos filhos e registra em stdout/stderr em vez de arquivos. Use CMD ["pm2-runtime", "ecosystem.config.cjs"] em seu Dockerfile.
Como monitoro processos PM2 de uma máquina remota?
PM2 Plus (serviço em nuvem de pagamento por processo) fornece um painel da web. Para monitoramento auto-hospedado, exponha as métricas do PM2 por meio do exportador Prometheus e visualize no Grafana. Para verificações de status simples, você pode usar SSH e executar pm2 status ou expor as métricas por meio de um endpoint HTTP que seu sistema de monitoramento pesquisa.
Qual é a diferença entre recarregar pm2 e reiniciar pm2?
pm2 restart mata todos os trabalhadores simultaneamente e os reinicia — há um breve período sem trabalhadores em execução (tempo de inatividade). pm2 reload é elegante: ele inicia novos trabalhadores, espera que eles estejam prontos e, em seguida, desliga trabalhadores antigos – tempo de inatividade zero. Use pm2 reload para implantações de produção. Nota: reload só funciona corretamente no modo cluster; o modo fork volta ao comportamento de reinicialização.
Como posso definir diferentes variáveis de ambiente para diferentes processos?
Cada processo em ecosystem.config.cjs possui suas próprias seções env e env_production. A seção env_production é usada quando você passa --env production para comandos PM2. Para segredos, nunca coloque-os diretamente no arquivo do ecossistema – configure-os no ambiente do sistema ou no arquivo .env.local e deixe o PM2 herdá-los. O sinalizador --update-env garante que o PM2 relê as variáveis de ambiente ao reiniciar.
Próximas etapas
PM2 é uma parte fundamental de qualquer implantação de produção do Node.js. ECOSIRE gerencia 5 processos PM2 em produção – Next.js, NestJS, Docusaurus e dois sites de marca – com reinicializações automáticas, rotação de log e implantações com tempo de inatividade zero em cada push to main.
Se você precisa de suporte de engenharia DevOps, arquitetura de implantação de produção ou ajuda na migração para uma configuração em contêiner, explore nossos serviços para ver como podemos ajudar.
Escrito por
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
Artigos Relacionados
AWS EC2 Deployment Guide for Web Applications
Complete AWS EC2 deployment guide: instance selection, security groups, Node.js deployment, Nginx reverse proxy, SSL, auto-scaling, CloudWatch monitoring, and cost optimization.
Zero-Downtime Database Migrations with Drizzle ORM
Run database migrations without downtime using Drizzle ORM. Covers expand-contract pattern, backward-compatible schema changes, rollback strategies, and CI/CD integration for PostgreSQL.
Next.js 16 App Router: Production Patterns and Pitfalls
Production-ready Next.js 16 App Router patterns: server components, caching strategies, metadata API, error boundaries, and performance pitfalls to avoid.