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

systemd com Node.js: Guia Prático

Atualizado em: 22 de agosto de 2026

Terminal Linux usado em desenvolvimento e administração de aplicações Node.js

Usar systemd com Node.js permite iniciar a aplicação automaticamente no boot, reiniciar após falhas, aplicar usuário restrito, definir variáveis, limitar recursos e integrar logs ao journal. Em servidores Linux, systemd é uma alternativa nativa a gerenciadores como PM2 quando você deseja controlar processos pelo sistema operacional.

Uma unit bem configurada descreve como o serviço inicia, quando deve reiniciar, quais diretórios pode acessar e como encerra. Configurações apressadas, como executar como root ou usar restart infinito sem limites, podem transformar uma falha simples em loop de consumo ou risco de segurança.

Neste guia, você aprenderá a criar uma unit, definir usuário, ambiente, working directory, restart, timeouts, hardening, logs, socket activation, deploy, health checks e graceful shutdown.

O que é systemd?

systemd é o gerenciador de sistema e serviços utilizado por muitas distribuições Linux. A documentação oficial de systemd.service descreve units de serviço. A documentação de systemd.exec apresenta usuário, ambiente, diretórios e isolamento.

Para comparar com um gerenciador específico de Node.js, consulte PM2 em produção com Node.js. Para encerramento correto, veja Graceful Shutdown no Node.js.

Estrutura de uma unit

Crie o arquivo:

/etc/systemd/system/my-api.service

Exemplo:

[Unit]
Description=My Node.js API
After=network.target

[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/srv/my-api/current
ExecStart=/usr/bin/node dist/server.js
Restart=on-failure
RestartSec=5s
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target

Recarregando units

sudo systemctl daemon-reload
sudo systemctl enable my-api
sudo systemctl start my-api

enable configura início no boot. start inicia agora.

Status

sudo systemctl status my-api

O comando mostra estado, PID, código de saída e linhas recentes do journal.

Não execute como root

Crie um usuário dedicado:

sudo useradd \
  --system \
  --home /srv/my-api \
  --shell /usr/sbin/nologin \
  myapp

O processo deve acessar apenas os arquivos necessários.

WorkingDirectory

Define o diretório de trabalho. Sem ele, caminhos relativos podem apontar para local inesperado:

WorkingDirectory=/srv/my-api/current

Prefira caminhos absolutos no código e na unit.

ExecStart

ExecStart=/usr/bin/node /srv/my-api/current/dist/server.js

Use o caminho real do Node.js. Não dependa do PATH de um shell interativo.

Node instalado pelo NVM

NVM é voltado a shells de usuário e não é carregado automaticamente pelo systemd. Para produção, prefira pacote do sistema, binário instalado em caminho estável ou imagem de container.

Environment

Environment=NODE_ENV=production
Environment=PORT=3000

Valores aparecem na unit e podem ser visíveis a administradores. Não coloque segredos diretamente no arquivo versionado.

EnvironmentFile

EnvironmentFile=/etc/my-api/environment

Arquivo:

NODE_ENV=production
PORT=3000
DATABASE_URL=...

Restrinja permissões:

sudo chown root:myapp /etc/my-api/environment
sudo chmod 640 /etc/my-api/environment

Consulte Variáveis de Ambiente no Node.js.

Type=simple

O processo iniciado por ExecStart é considerado o serviço principal. Aplicações Node.js comuns devem permanecer em primeiro plano e não fazer daemonize.

Restart

Restart=on-failure
RestartSec=5s

on-failure reinicia em saída não zero, sinal ou timeout. Um encerramento normal não reinicia.

Restart=always

Esse modo reinicia até após saída normal. Pode ser útil para serviços permanentes, mas dificulta parar por lógica da própria aplicação se a operação não usar systemctl.

Protegendo contra loops

StartLimitIntervalSec=60
StartLimitBurst=5

Depois de muitas falhas, systemd interrompe tentativas. Isso evita loop rápido consumindo CPU e logs.

Resetando estado de falha

sudo systemctl reset-failed my-api
sudo systemctl start my-api

Corrija a causa antes de reiniciar repetidamente.

Sinais

Ao parar, systemd envia SIGTERM ao processo principal. A aplicação deve fechar servidor e dependências:

process.on('SIGTERM', async () => {
  server.close(async () => {
    await database.end();
    process.exitCode = 0;
  });
});

TimeoutStopSec

TimeoutStopSec=30s

Se o processo não encerra no prazo, systemd envia sinal final. Alinhe com o timeout da aplicação e do balanceador.

KillSignal

KillSignal=SIGTERM

SIGTERM já é comum. Não use SIGKILL como primeiro sinal, pois impede cleanup.

KillMode

Se a aplicação cria processos filhos, KillMode=control-group ajuda a encerrar o grupo. Não deixe workers órfãos.

TimeoutStartSec

TimeoutStartSec=30s

Com Type=simple, o serviço é considerado iniciado imediatamente. Para readiness explícita, avalie Type=notify e integração com sd_notify.

Readiness

systemd saber que o processo existe não significa que a API está pronta. O proxy ou monitor precisa consultar uma rota de readiness.

Veja Health Checks no Node.js.

Logs no journal

stdout e stderr são capturados:

sudo journalctl -u my-api
sudo journalctl -u my-api -f

Use logs estruturados e não grave arquivos diretamente se o journal já é coletado.

Consulte Console no Node.js.

Identificador de log

SyslogIdentifier=my-api

Ajuda a filtrar mensagens.

Rotação

journald possui sua própria política de armazenamento. Configure limites globais ou por ambiente e encaminhe para uma plataforma central.

LimitNOFILE

LimitNOFILE=65536

Serviços com muitas conexões podem precisar de mais descritores. Aumente apenas depois de medir e ajustar limites do sistema.

MemoryMax

MemoryMax=1G

O cgroup limita memória total. Quando excedido, o processo pode ser encerrado. Configure também limite de heap do V8 de forma compatível.

CPUQuota

CPUQuota=200%

Em sistemas multicore, 200% representa até dois núcleos de CPU.

TasksMax

TasksMax=256

Limita threads e processos no cgroup. Node.js, thread pool, workers e bibliotecas nativas precisam caber.

Hardening básico

NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true

Essas opções reduzem acesso. Teste porque a aplicação pode precisar de diretórios específicos.

ReadWritePaths

ReadWritePaths=/srv/my-api/uploads /var/lib/my-api

Com ProtectSystem=strict, libere apenas caminhos necessários.

StateDirectory

StateDirectory=my-api

systemd pode criar diretório em /var/lib/my-api com ownership apropriado.

RuntimeDirectory

RuntimeDirectory=my-api

Cria diretório temporário em /run, útil para sockets Unix e PIDs auxiliares.

ProtectKernelTunables

ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true

Essas opções evitam alterações no host por um processo comprometido.

RestrictAddressFamilies

RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX

Permita apenas famílias necessárias.

CapabilityBoundingSet

Para porta abaixo de 1024, prefira proxy reverso em vez de conceder capabilities ao Node.js. O Nginx pode escutar 443 e encaminhar para 3000.

Veja Nginx como Proxy para Node.js.

Permission Model

O hardening do systemd atua no sistema operacional. O Permission Model no Node.js pode adicionar outra camada dentro do runtime.

ExecStartPre

ExecStartPre=/usr/bin/test -f /srv/my-api/current/dist/server.js

Use verificações rápidas e determinísticas. Não execute migration longa automaticamente em todas as instâncias.

ExecReload

Node.js normalmente não recarrega código com sinal de reload. Prefira restart coordenado ou deploy blue-green.

Reiniciando após deploy

sudo systemctl restart my-api

Esse comando interrompe e inicia. Para zero downtime, use duas instâncias ou sockets.

Template units

/etc/systemd/system/my-api@.service

É possível iniciar instâncias:

sudo systemctl start my-api@1 my-api@2

Cada instância precisa de porta ou socket diferente.

Environment por instância

EnvironmentFile=/etc/my-api/%i.env

%i representa o identificador da instância.

Socket activation

Uma unit .socket pode abrir o socket antes da aplicação e entregar o descritor ao serviço. Isso permite fila durante restart, mas exige suporte no aplicativo.

Unix socket

Nginx pode encaminhar para um socket Unix em /run/my-api/app.sock. Configure permissões por grupo e remova sockets antigos.

Dependências de rede

After=network-online.target
Wants=network-online.target

Mesmo assim, banco e DNS podem ficar indisponíveis depois. A aplicação precisa de retries e health checks.

Não use After para banco remoto

systemd no host não conhece o estado real de um PostgreSQL externo. Implemente conexão resiliente e readiness.

Banco de dados

Durante shutdown, encerre pools. Consulte Pool PostgreSQL no Node.js.

/srv/my-api/releases/20260822-120000
/srv/my-api/current -> releases/20260822-120000

Depois de atualizar o symlink, reinicie o serviço. Mantenha release anterior para rollback.

Permissões do deploy

O usuário de deploy pode atualizar releases, enquanto o usuário myapp apenas lê código. Separe privilégios.

Rollback

Reaponte o symlink para a versão anterior e reinicie. Se houve migration incompatível, rollback de código pode não ser suficiente.

Monitorando estado

systemctl is-active my-api
systemctl show my-api -p MainPID -p NRestarts

Integre com monitoramento, mas também verifique a rota HTTP.

Watchdog

Serviços Type=notify podem enviar heartbeats ao systemd. Se o processo para de responder, o watchdog reinicia. Isso exige integração correta e não substitui métricas externas.

OOM

Analise journal e kernel quando o processo é morto por memória. Reiniciar sem corrigir limite ou vazamento apenas repete o incidente.

Core dumps

Falhas nativas podem gerar core dump conforme configuração. Trate o arquivo como sensível, pois pode conter dados e segredos.

Testando a unit

Valide:

  • boot automático;
  • reinício após falha;
  • limite de tentativas;
  • SIGTERM;
  • timeout de parada;
  • usuário restrito;
  • diretórios somente leitura;
  • logs;
  • memória máxima;
  • rollback.

Comandos úteis

sudo systemctl start my-api
sudo systemctl stop my-api
sudo systemctl restart my-api
sudo systemctl reload-or-restart my-api
sudo systemctl enable my-api
sudo systemctl disable my-api
sudo journalctl -u my-api --since today

Erros comuns

  • Executar como root: uma falha ganha privilégios amplos.
  • Usar NVM no ExecStart: caminho não existe no ambiente.
  • Restart sem limite: o serviço entra em loop.
  • Sem SIGTERM: conexões são interrompidas.
  • Segredo na unit: configuração fica exposta.
  • ProtectSystem sem exceções: a aplicação não grava onde precisa.
  • Confiar só em is-active: processo vivo pode não estar pronto.

Boas práticas

  • Use usuário dedicado.
  • Use caminhos absolutos.
  • Configure restart com limites.
  • Trate SIGTERM.
  • Defina timeouts.
  • Envie logs para stdout e stderr.
  • Aplique hardening gradual.
  • Limite memória e descritores.
  • Use health checks externos.
  • Mantenha rollback.

Conclusão

Usar systemd com Node.js oferece gerenciamento nativo de processo, boot automático, reinício, limites e isolamento. Uma unit simples já melhora disponibilidade; opções de hardening e cgroups adicionam proteção operacional.

O serviço deve rodar como usuário restrito, permanecer em primeiro plano, registrar em stdout e tratar SIGTERM. Com limites de restart, health checks, caminhos estáveis e deploy reversível, systemd mantém a aplicação previsível sem depender de um gerenciador adicional.

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