Todos os artigos
169 artigos · atualizado semanalmente Veja nossas Ferramentas
Todos os artigos
Tutoriais

systemd: como criar e gerenciar serviços

systemd: aprenda a criar e gerenciar serviços com aplicações práticas e recomendações para quem está começando ou quer revisar o básico.

systemd: como criar e gerenciar serviços
COVER · Tutoriais

Você sobe a aplicação na VPS com nohup node server.js &, fecha o SSH e vai dormir tranquilo. De manhã o serviço está fora do ar: o processo morreu durante a noite e ninguém o reergueu, ou a sessão SSH caiu e levou tudo junto. Pior cenário: a máquina reiniciou por causa de um update de kernel e sua aplicação simplesmente não voltou. nohup e screen foram feitos para manter um comando vivo enquanto você não está olhando, não para gerenciar um serviço de produção. Para isso existe o systemd, e ele já está instalado na sua distro.

Este artigo mostra como transformar uma aplicação num serviço gerenciado de verdade: que reinicia sozinho, sobe no boot e tem log centralizado. Se em algum momento a máquina ficar sem espaço por causa de log mal configurado, vale ler depois como investigar falta de espaço em disco no Linux — journald cheio é uma causa comum.

Por que systemd em vez de nohup ou screen

nohup e screen/tmux não monitoram o processo. Se ele crashar, fica morto. Não sobem no boot. Não têm log estruturado. Não controlam dependências de ordem de inicialização. O systemd faz tudo isso: reinicia em caso de falha, integra com o boot, centraliza logs no journald, e expressa que seu serviço depende da rede ou do banco estar de pé.

A troca é uma única vez: você escreve um arquivo de unit e nunca mais pensa em manter o processo vivo na mão.

Anatomia de um arquivo .service

Um unit fica em /etc/systemd/system/<nome>.service e tem três seções:

  • [Unit] — metadados e dependências: descrição, o que precisa estar de pé antes.
  • [Service] — como rodar: o comando, o usuário, política de restart, variáveis de ambiente.
  • [Install] — quando habilitado no boot, em qual "target" ele se enquadra.

Aqui um exemplo real para uma aplicação Node:

[Unit]
Description=API Quick Tools
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=deploy
Group=deploy
WorkingDirectory=/opt/quicktools
EnvironmentFile=/opt/quicktools/.env
ExecStart=/usr/bin/node /opt/quicktools/server.js
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Cada linha aqui resolve um problema que nohup deixava em aberto: roda como usuário não-root, lê env de arquivo, reinicia em falha com 5 segundos de espera, e sobe no boot.

Restart=: a política que separa serviço de script

A diretiva Restart= define quando o systemd reergue o processo:

  • no — nunca reinicia (default). É efetivamente um nohup.
  • on-failure — reinicia só se o processo sair com código != 0 ou for morto por sinal. É o que você quer na maioria dos casos.
  • always — reinicia até em saída limpa (código 0). Útil para serviços que nunca deveriam terminar, mas mascara bugs de saída intencional.

Combine com RestartSec= para não entrar em loop de restart instantâneo quando o serviço crasha na inicialização. E se quiser um teto, StartLimitIntervalSec + StartLimitBurst no [Unit] impedem que o systemd fique reiniciando um serviço quebrado infinitamente.

journalctl: onde foram parar os logs

Como o exemplo manda stdout/stderr para journal, todo log vai para o journald. Os comandos do dia a dia:

# logs do serviço, do mais recente
journalctl -u quicktools.service -n 100 --no-pager

# acompanhar em tempo real (tipo tail -f)
journalctl -u quicktools.service -f

# só de hoje, com prioridade de erro
journalctl -u quicktools.service --since today -p err

Não há mais arquivo de log perdido em /var/log ou redirecionado para nohup.out. Tudo fica indexado por unit, por tempo e por prioridade. O preço é gerenciar o tamanho do journal — daí a relação com falta de espaço em disco.

enable vs start: a confusão clássica

Os dois comandos parecem iguais e fazem coisas diferentes:

  • systemctl start quicktools — inicia agora, nesta sessão. Não sobrevive a reboot.
  • systemctl enable quicktools — registra para subir no próximo boot. Não inicia agora.

Você quase sempre quer os dois. O atalho é systemctl enable --now quicktools: habilita no boot e inicia imediatamente. Esquecer o enable é o motivo número um de "funcionou, reiniciei a máquina e sumiu".

Lendo status e diagnosticando

systemctl status quicktools é o primeiro comando quando algo dá errado:

systemctl status quicktools.service

Ele mostra se está active (running), failed, ou inactive, o PID, há quanto tempo está de pé, e as últimas linhas de log. Se aparecer failed, vá direto para o journalctl -u para ver o stack trace.

Pegadinhas comuns

Três coisas que travam quase todo mundo na primeira vez:

  • daemon-reload: depois de editar o .service, rode systemctl daemon-reload antes de reiniciar o serviço. Sem isso o systemd continua usando a versão antiga em cache e você jura que sua mudança não fez nada.
  • WorkingDirectory: se a aplicação lê arquivos por caminho relativo, defina WorkingDirectory explicitamente. O systemd inicia no / por padrão, não no diretório do binário, e caminhos relativos quebram.
  • Variáveis de ambiente: o systemd não carrega seu ~/.bashrc nem o .env automaticamente. Use Environment= para variáveis pontuais ou EnvironmentFile= apontando para um arquivo. PATH também é mínimo — por isso o ExecStart usa caminho absoluto para o binário.

Perguntas frequentes

Qual a diferença entre enable e start no systemd?

start inicia o serviço imediatamente na sessão atual e não sobrevive a reboot. enable registra o serviço para subir automaticamente no próximo boot, mas não o inicia agora. Use systemctl enable --now <serviço> para fazer os dois de uma vez.

Por que minha alteração no arquivo .service não fez efeito?

Você provavelmente esqueceu o systemctl daemon-reload. O systemd mantém os units em cache na memória e só relê os arquivos quando você manda recarregar. Rode daemon-reload e depois restart do serviço sempre que editar o unit.

Devo usar Restart=always ou Restart=on-failure?

on-failure na maioria dos casos: reinicia só quando o processo sai com erro ou é morto por sinal, e respeita uma saída limpa. always reinicia até em saída código 0, o que é útil para serviços que nunca deveriam terminar, mas mascara situações em que o processo terminou de propósito.

O systemd carrega meu .env automaticamente?

Não. Ele não lê ~/.bashrc nem nenhum .env por conta própria. Você precisa declarar EnvironmentFile=/caminho/.env na seção [Service], ou definir variáveis uma a uma com Environment=.

O que fica do systemd no seu fluxo

Trocar nohup por systemd é meia hora de trabalho que se paga na primeira madrugada em que o serviço crasha e reergue sozinho sem te acordar. Escreva o unit com WorkingDirectory e EnvironmentFile corretos, escolha Restart=on-failure com RestartSec, rode enable --now, e use journalctl -u -f quando precisar olhar. Lembre do daemon-reload toda vez que editar o arquivo. A partir daí seu serviço sobe no boot, sobrevive a crashes e tem log centralizado — três coisas que nohup & nunca vai te dar.

RD
Autor
Rafael Duarte
Desenvolvedor backend com passagem por fintech e SaaS B2B — trabalhou em times que escalaram APIs de zero a milhões de requisições. Carrega cicatrizes de produção suficientes para ter opiniões fortes sobre ferramentas, padrões e decisões de arquitetura. Não é acadêmico: leu a RFC do UUID quando precisou escolher entre v4 e v7 para uma tabela de alta escrita.
Ver perfil