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.serviceExemplo:
[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.targetRecarregando units
sudo systemctl daemon-reload
sudo systemctl enable my-api
sudo systemctl start my-apienable configura início no boot. start inicia agora.
Status
sudo systemctl status my-apiO 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 \
myappO 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/currentPrefira caminhos absolutos no código e na unit.
ExecStart
ExecStart=/usr/bin/node /srv/my-api/current/dist/server.jsUse 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=3000Valores aparecem na unit e podem ser visíveis a administradores. Não coloque segredos diretamente no arquivo versionado.
EnvironmentFile
EnvironmentFile=/etc/my-api/environmentArquivo:
NODE_ENV=production
PORT=3000
DATABASE_URL=...Restrinja permissões:
sudo chown root:myapp /etc/my-api/environment
sudo chmod 640 /etc/my-api/environmentConsulte 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=5son-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=5Depois 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-apiCorrija 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=30sSe o processo não encerra no prazo, systemd envia sinal final. Alinhe com o timeout da aplicação e do balanceador.
KillSignal
KillSignal=SIGTERMSIGTERM 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=30sCom 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 -fUse logs estruturados e não grave arquivos diretamente se o journal já é coletado.
Consulte Console no Node.js.
Identificador de log
SyslogIdentifier=my-apiAjuda 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=65536Serviços com muitas conexões podem precisar de mais descritores. Aumente apenas depois de medir e ajustar limites do sistema.
MemoryMax
MemoryMax=1GO 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=256Limita 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-apiCom 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=trueEssas opções evitam alterações no host por um processo comprometido.
RestrictAddressFamilies
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIXPermita 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.jsUse 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-apiEsse 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@2Cada 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.targetMesmo 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.
Deploy com symlink
/srv/my-api/releases/20260822-120000
/srv/my-api/current -> releases/20260822-120000Depois 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 NRestartsIntegre 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 todayErros 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.



