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

Health Checks no Node.js

Atualizado em: 30 de setembro de 2026

Rack de servidores processando fluxos de dados no Node.js

Health checks são endpoints ou sinais usados para indicar se uma aplicação Node.js está viva, pronta para receber tráfego e funcionando de acordo com critérios operacionais. Eles orientam balanceadores, orquestradores, supervisores e equipes de suporte.

Um health check mal projetado pode causar mais indisponibilidade que o problema original. Se a verificação depende de todos os serviços externos, uma falha parcial pode retirar todas as réplicas do ar. O desenho deve separar liveness, readiness e startup.

Liveness

Liveness responde: “o processo está vivo e capaz de continuar?”. Ela deve ser simples e local. Quando falha de forma sustentada, a plataforma pode reiniciar o container.

app.get('/health/live', (req, res) => {
  res.json({ status: 'alive' });
});

Não consulte banco ou API externa na liveness. Uma dependência indisponível não significa necessariamente que reiniciar o processo resolverá.

Readiness

Readiness responde: “esta instância pode receber tráfego agora?”. Ela pode considerar inicialização, shutdown, pools essenciais e estado interno.

let ready = false;
let shuttingDown = false;

app.get('/health/ready', (req, res) => {
  if (!ready || shuttingDown) {
    res.status(503).json({ status: 'not-ready' });
    return;
  }

  res.json({ status: 'ready' });
});

Ao iniciar, defina ready=true apenas depois de concluir etapas críticas. No shutdown, defina shuttingDown=true antes de fechar o servidor.

Startup probe

Aplicações com bootstrap demorado podem usar uma verificação de startup. Enquanto ela não passa, a plataforma evita aplicar liveness agressiva. Isso impede reinícios em loop durante migrations, aquecimento ou carregamento de modelos.

Health check detalhado

app.get('/health', async (req, res) => {
  const checks = await runChecks();
  const healthy = checks.every((check) => check.status === 'up');

  res.status(healthy ? 200 : 503).json({
    status: healthy ? 'up' : 'down',
    checks,
  });
});

Esse endpoint pode ser útil para operadores, mas não deve necessariamente controlar o balanceamento. Proteja detalhes internos e aplique timeout.

Dependências essenciais e opcionais

Classifique dependências:

  • essencial: sem ela nenhuma rota principal funciona;
  • degradável: a aplicação consegue oferecer resposta parcial;
  • opcional: recurso secundário pode ficar indisponível;
  • assíncrona: fila ou sistema que pode acumular temporariamente.

Readiness não deve falhar por uma integração opcional se o serviço ainda entrega seu objetivo principal.

Check de banco de dados

async function checkDatabase({ signal }) {
  const start = performance.now();
  try {
    await database.query('SELECT 1', { signal });
    return {
      name: 'database',
      status: 'up',
      durationMs: performance.now() - start,
    };
  } catch (error) {
    return {
      name: 'database',
      status: 'down',
      durationMs: performance.now() - start,
      error: error.name,
    };
  }
}

Use uma consulta barata e limite tempo. Não registre credenciais, SQL sensível ou mensagem completa no corpo público.

Timeout global

async function runChecks() {
  const signal = AbortSignal.timeout(1000);

  return Promise.all([
    checkDatabase({ signal }),
    checkRedis({ signal }),
  ]);
}

Sem timeout, a própria verificação pode ficar pendurada, fazendo o balanceador interpretar como falha após o prazo externo.

Não sobrecarregue dependências

Se dezenas de réplicas recebem probes a cada segundo, o banco pode receber milhares de checks. Estratégias:

  • cache curto do resultado;
  • intervalo adequado;
  • consulta mínima;
  • jitter entre instâncias;
  • limite de concorrência;
  • check passivo baseado em uso real.

Cacheando resultado

let cached = null;
let expiresAt = 0;

async function cachedReadiness() {
  if (Date.now() < expiresAt && cached) return cached;

  cached = await calculateReadiness();
  expiresAt = Date.now() + 2000;
  return cached;
}

Use proteção contra várias atualizações simultâneas. O TTL cria uma janela de desatualização, por isso deve ser pequeno e intencional.

Check passivo

Em vez de consultar uma dependência apenas para health check, registre o resultado das operações reais. Se o pool está conectado e consultas recentes tiveram sucesso, readiness pode usar esse estado.

Combine sinal passivo com uma verificação ativa periódica para detectar ausência de tráfego.

Event loop

Um processo pode responder ao endpoint e ainda estar saturado. Acompanhe event loop utilization e delay em métricas. Evite tornar readiness extremamente sensível a um único pico, pois retirar instâncias aumenta a carga nas restantes.

Memória

Readiness pode considerar pressão de memória antes de OOM, mas use hysteresis:

const HIGH_RSS = 1_500_000_000;
const LOW_RSS = 1_300_000_000;
let memoryReady = true;

function updateMemoryState() {
  const rss = process.memoryUsage().rss;
  if (memoryReady && rss > HIGH_RSS) memoryReady = false;
  if (!memoryReady && rss < LOW_RSS) memoryReady = true;
}

Sem faixas distintas, o estado pode oscilar. Investigue a causa do crescimento.

Fila interna

Se a aplicação usa fila limitada, readiness pode falhar quando não há capacidade para novas requisições. Novamente, aplique hysteresis e confirme que remover a instância permitirá drenagem.

Degraded

Um endpoint operacional pode retornar degraded quando recursos opcionais falham:

{
  "status": "degraded",
  "checks": [
    { "name": "database", "status": "up" },
    { "name": "recommendations", "status": "down" }
  ]
}

Para o balanceador, a instância pode continuar pronta. Para dashboards, o estado degradado gera alerta.

Status HTTP

  • 200: verificação passou;
  • 503: instância não está disponível;
  • 500: normalmente evite, pois falha é estado esperado do check;
  • 401/403: pode quebrar probes se autenticação não estiver configurada.

Endpoints do orquestrador podem ficar em porta ou rede interna sem autenticação, desde que não sejam expostos publicamente.

Informações no corpo

Exponha o mínimo:

{
  "status": "ready"
}

Versão, commit, nomes de dependências e erros detalhados devem ficar em endpoint autenticado ou logs. Isso reduz reconhecimento da infraestrutura por atacantes.

Build info

Um endpoint interno pode fornecer:

app.get('/internal/info', requireInternal, (req, res) => {
  res.json({
    version: process.env.APP_VERSION,
    commit: process.env.GIT_SHA,
    startedAt,
  });
});

Não misture info com readiness se o balanceador só precisa do status.

Kubernetes

livenessProbe:
  httpGet:
    path: /health/live
    port: 3000
  periodSeconds: 10
  timeoutSeconds: 2

readinessProbe:
  httpGet:
    path: /health/ready
    port: 3000
  periodSeconds: 5
  timeoutSeconds: 2

Ajuste thresholds e startupProbe ao tempo real. Probes agressivas podem reiniciar aplicações durante pausas temporárias.

Docker Compose

healthcheck:
  test: ["CMD", "node", "scripts/healthcheck.js"]
  interval: 10s
  timeout: 3s
  retries: 3

Não dependa de curl em imagens que não o incluem. Um script Node.js pode fazer a chamada.

Load balancer

Configure caminho, timeout, intervalo, códigos esperados e número de falhas. Uma divergência entre plataforma e aplicação pode manter instância ruim ou remover instância saudável.

Graceful shutdown

O fluxo correto:

  1. receber SIGTERM;
  2. marcar readiness como 503;
  3. aguardar retirada do balanceador;
  4. parar de aceitar conexões;
  5. drenar requisições;
  6. fechar recursos;
  7. encerrar.

Health check e autoscaling

Health check não é métrica de scaling. Use CPU, fila, latência ou métricas de negócio. Retirar instâncias por sobrecarga pode agravar o problema se todas ficarem não prontas.

Check de disco

Se a aplicação precisa escrever localmente, verifique capacidade e permissões de forma controlada. Não crie arquivo a cada probe. Uma checagem periódica em background pode atualizar estado.

Check de DNS

Falhas de DNS afetam chamadas externas, mas consultar DNS em toda probe adiciona dependência. Monitore erros reais e use verificação periódica separada.

Check de migração

Readiness pode depender da versão mínima do schema:

const compatible = databaseSchemaVersion >= MIN_SCHEMA_VERSION;

Não execute migrations dentro do health endpoint.

Testes

Teste:

  • startup antes de ready;
  • banco indisponível;
  • dependência opcional indisponível;
  • timeout;
  • shutdown;
  • resultado cacheado;
  • recuperação após falha;
  • oscilações;
  • alta frequência de probes.

Observabilidade

Meça duração e resultado dos checks, mudanças de estado, tempo não pronto e motivos. Não use path de probe nas métricas de latência de usuário sem separação, pois ele pode distorcer volume.

Alertas

Alerta deve considerar impacto agregado:

  • percentual de réplicas prontas;
  • tempo contínuo;
  • erro do balanceador;
  • SLO de usuário;
  • dependência compartilhada;
  • reinícios.

Erros comuns

  • usar banco na liveness;
  • falhar readiness por dependência opcional;
  • executar checks caros a cada segundo;
  • não definir timeout;
  • expor detalhes internos;
  • não marcar not-ready no shutdown;
  • usar health check para autoscaling;
  • oscilar sem hysteresis;
  • não testar configuração da plataforma;
  • reiniciar por falha que restart não resolve.

Fluxo recomendado

Separe liveness, readiness e startup, mantenha liveness local, classifique dependências, limite custo e integre readiness ao shutdown. Combine com Graceful Shutdown, Event Loop Utilization, Process Reports e métricas de perf_hooks.

Consulte a documentação oficial de probes do Kubernetes e a documentação oficial do servidor HTTP do Node.js.

10 melhores cursos de programação em 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