Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Graceful Shutdown no Node.js

Atualizado em: 5 de agosto de 2026

Botão vermelho representando AbortController no Node.js

Durante deploy, reinício, escalonamento ou manutenção, o processo Node.js recebe um pedido para encerrar. Se ele termina imediatamente, requisições podem ser interrompidas, mensagens podem ficar sem confirmação e arquivos podem permanecer incompletos. O Graceful Shutdown no Node.js organiza o encerramento para parar novas tarefas, concluir trabalho em andamento e liberar recursos dentro de um limite.

Um desligamento seguro não significa esperar para sempre. O processo precisa de uma janela máxima e uma estratégia para tarefas que não terminam. Também deve lidar com múltiplos sinais, erros fatais e dependências como banco, filas, sockets e workers.

Neste guia, você aprenderá a tratar SIGTERM e SIGINT, fechar servidor HTTP, rastrear conexões, cancelar tarefas, drenar filas, definir timeout forçado e testar o fluxo.

Por que o processo recebe SIGTERM?

Orquestradores e gerenciadores normalmente enviam SIGTERM antes de encerrar o contêiner ou processo. SIGINT é comum quando o desenvolvedor pressiona Ctrl+C. O Node.js permite registrar listeners:

process.on('SIGTERM', () => {
  console.log('SIGTERM recebido');
});

process.on('SIGINT', () => {
  console.log('SIGINT recebido');
});

A documentação oficial de sinais do processo descreve diferenças entre plataformas. Para servidores, consulte a documentação do módulo HTTP.

Veja também o que é Node.js e como criar uma API com Node.js.

Estrutura básica

let shuttingDown = false;

async function shutdown(signal) {
  if (shuttingDown) return;
  shuttingDown = true;

  console.log(`Encerrando por ${signal}`);

  try {
    await closeResources();
    process.exitCode = 0;
  } catch (error) {
    console.error(error);
    process.exitCode = 1;
  }
}

process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));

A flag impede duas execuções concorrentes. Prefira definir exitCode e permitir que o event loop esvazie, em vez de chamar process.exit() cedo demais.

Fechando o servidor HTTP

function closeServer(server) {
  return new Promise((resolve, reject) => {
    server.close(error => {
      if (error) reject(error);
      else resolve();
    });
  });
}

server.close() para novas conexões e aguarda conexões elegíveis. O comportamento com keep-alive depende da versão e das APIs usadas. Versões atuais possuem métodos para fechar conexões ociosas.

Removendo a instância do tráfego

Antes de fechar, marque o serviço como não pronto. O load balancer precisa parar de enviar requisições. Um endpoint de readiness pode mudar de 200 para 503:

let ready = true;

app.get('/ready', (req, res) => {
  res.sendStatus(ready ? 200 : 503);
});

async function shutdown() {
  ready = false;
  await delay(3000);
  await closeServer(server);
}

O pequeno atraso permite que a mudança se propague. Ajuste conforme o intervalo de health check da infraestrutura.

Rastreando sockets

const sockets = new Set();

server.on('connection', socket => {
  sockets.add(socket);
  socket.on('close', () => sockets.delete(socket));
});

Após a janela de drenagem, destrua conexões remanescentes:

for (const socket of sockets) {
  socket.destroy();
}

Faça isso apenas depois do prazo, pois destruir imediatamente anula o encerramento gradual.

Timeout global

const forceTimer = setTimeout(() => {
  console.error('Timeout do graceful shutdown');
  process.exit(1);
}, 30_000);

forceTimer.unref();

O timeout deve ser menor que a janela concedida pelo orquestrador. unref() impede que o timer mantenha o processo vivo quando todo o restante já terminou.

Cancelando tarefas internas

Um controlador global comunica encerramento:

const shutdownController = new AbortController();

function beginShutdown() {
  shutdownController.abort(
    new Error('Aplicação em encerramento')
  );
}

Propague o sinal para timers, fetch, streams e funções próprias. O guia de AbortController no Node.js explica o padrão.

Fechando banco de dados

await databasePool.end();

Pare de aceitar novas consultas antes de fechar o pool. Aguarde transações em andamento, mas aplique um limite. Não encerre a conexão no meio de uma transação sem entender rollback e consistência.

Drenando filas

Consumidores devem parar de reservar novos trabalhos, concluir ou devolver os atuais e fechar a conexão:

await worker.pause();
await worker.close();
await queue.close();

A semântica depende da biblioteca. Confirme se close() espera tarefas, cancela ou apenas fecha o cliente.

Streams e arquivos

Espere pipelines em andamento ou cancele com sinal e remova arquivos parciais. Veja Streams no Node.js e File System no Node.js.

WebSocket e conexões longas

Pare upgrades, informe os clientes e feche conexões com código apropriado. Uma janela curta pode ser insuficiente para sessões persistentes; clientes precisam implementar reconexão com backoff.

Worker Threads e processos filhos

Envie mensagem de encerramento cooperativo. Aguarde confirmação e use terminate() ou sinais apenas após timeout. Consulte Worker Threads e Child Process no Node.js.

Ordem de fechamento

Uma ordem comum:

  1. marcar readiness como falsa;
  2. parar novas conexões e novas tarefas;
  3. abortar trabalho não essencial;
  4. aguardar requisições e jobs;
  5. fechar filas, banco, cache e telemetria;
  6. destruir conexões restantes após limite;
  7. definir código de saída.

A ordem evita fechar o banco enquanto rotas ainda executam consultas.

Erros fatais

uncaughtException e unhandledRejection podem indicar estado inconsistente. Registre o erro, inicie encerramento limitado e permita que o supervisor reinicie. Não continue atendendo indefinidamente após uma exceção desconhecida.

process.on('uncaughtException', error => {
  logger.fatal({ error }, 'Exceção não tratada');
  shutdown('uncaughtException');
});

O handler também pode falhar; mantenha-o simples.

Não chame process.exit() cedo

process.exit() interrompe o event loop e pode cortar logs, stdout e gravações pendentes. Use apenas no timeout forçado ou quando não existe forma segura de continuar.

Telemetria

Antes de terminar, faça flush de logs, métricas e traces com limite. Não permita que uma plataforma de observabilidade indisponível impeça o encerramento.

Idempotência

O encerramento pode receber vários sinais. Cada função de fechamento deve tolerar chamada repetida ou o coordenador deve garantir execução única. Trate erros “já fechado” de forma previsível.

Testando o shutdown

Inicie uma requisição lenta, envie SIGTERM e confirme que:

  • readiness fica indisponível;
  • novas requisições deixam de chegar;
  • a requisição atual termina;
  • o banco fecha depois da rota;
  • o processo sai dentro do prazo;
  • uma tarefa travada é forçada após timeout.

Teste em contêiner e no mesmo gerenciador usado em produção.

Observabilidade

Registre sinal, horário de início, recursos pendentes, duração de cada etapa, timeout e código de saída. Uma métrica de encerramentos forçados revela recursos que não fecham corretamente.

Erros comuns

  • Chamar process.exit() logo após SIGTERM.
  • Fechar banco antes do servidor.
  • Não remover a instância do load balancer.
  • Esperar para sempre por uma tarefa.
  • Executar shutdown duas vezes.
  • Ignorar WebSockets e keep-alive.
  • Não fazer flush de telemetria.
  • Usar prazo maior que o do orquestrador.

Boas práticas

  • Trate SIGTERM e SIGINT.
  • Marque readiness como falsa primeiro.
  • Pare novas tarefas antes de fechar dependências.
  • Use AbortSignal para cancelamento cooperativo.
  • Defina timeout global.
  • Feche recursos na ordem correta.
  • Torne o processo idempotente.
  • Monitore encerramentos forçados.
  • Teste em ambiente realista.
  • Deixe o supervisor reiniciar após erro fatal.

Conclusão

O Graceful Shutdown no Node.js protege requisições, mensagens e dados durante deploys e reinícios. Ele coordena readiness, servidor, tarefas e dependências antes de permitir a saída.

O processo precisa ser gradual, mas limitado. Pare novas entradas, conclua o que for seguro e force o encerramento após o prazo. Com sinais propagados e observabilidade, deploys se tornam previsíveis e deixam de causar falhas intermitentes.

Os 10 Melhores Cursos de Programação de 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita