Um serviço systemd que não inicia quase nunca é mistério. Rode systemctl status <unit>, depois leia as últimas 50 linhas do journal dessa unidade com journalctl -u <unit> -n 50 --no-pager — entre os dois, uma de cinco causas costuma ser nomeada com todas as letras: um caminho errado, um binário ausente, permissões erradas, uma negação do SELinux/AppArmor ou um erro de sintaxe no unit file. O motivo da falha está no log; as correções abaixo são só casamento de padrões contra ele.
Como ver por que um serviço systemd falhou?
Status primeiro, journal depois:
systemctl status myapp.service
journalctl -u myapp.service -n 50 --no-pager O status te dá o estado (inactive (dead), failed (exit-code), activating (auto-restart)) e as últimas linhas de log. O journal te dá a história completa: stdout, stderr e as reclamações do próprio systemd sobre a unidade.
Se a unidade falhou antes e você quer o registro daquela execução, adicione -b para o boot atual ou --since today:
journalctl -u myapp.service -b --no-pager Para o kit completo — boots, prioridades, seguir o output ao vivo — veja o journalctl cheat sheet. É a mesma memória muscular.
Mais um par que vale conhecer:
systemctl list-units --failed
systemctl reset-failed myapp.service O primeiro lista toda unidade vermelha da máquina. O segundo limpa o estado de falha depois do conserto — cosmético, mas evita páginas de status chorando lobo.
Quais são as causas mais comuns?
Depois que o log nomeia o sintoma, a causa quase sempre é uma destas cinco:
| O log diz | Causa provável | Correção |
|---|---|---|
status=203/EXEC | Caminho errado no ExecStart= ou interpretador ausente | Caminho absoluto, chmod +x, confira o shebang |
status=203/EXEC num script | Script com line endings CRLF ou shebang errado | dos2unix script.sh, conserte a primeira linha |
status=1/FAILURE, sem log da app | Diretório de trabalho ou env var faltando | Defina WorkingDirectory=, adicione Environment= |
Permission denied | O usuário não consegue ler arquivos nem bindar a porta | Conserte o dono; portas abaixo de 1024 pedem AmbientCapabilities=CAP_NET_BIND_SERVICE ou root |
Unit is masked | Alguém rodou systemctl mask | systemctl unmask myapp.service |
| Edição no unit file “não faz nada” | Daemon não recarregado | systemctl daemon-reload |
A família 203/EXEC merece menção especial porque é a que devora tardes. O systemd não usa seu shell para lançar o ExecStart=. Isso significa:
# Errado — sem shell, ~ nunca expande, sem busca no PATH
ExecStart=~/app/run.sh
# Certo
ExecStart=/opt/app/run.sh E o script em si precisa ser executável e começar com um shebang de verdade (#!/bin/bash ou #!/usr/bin/env bash). Um script que roda bem no seu terminal mas falha com 203 sob systemd é quase sempre um destes: não executável, line endings CRLF, shebang apontando para lugar nenhum ou caminho relativo.
Por que ele inicia na mão mas não no boot?
O bug clássico de ordenação. Se o log mostra falhas logo depois do boot mas a unidade inicia bem quando você roda systemctl start à mão, seu serviço está perdendo uma corrida — ele busca a rede, um disco montado ou um banco de dados antes disso existir.
A correção é declarar dependências em vez de torcer:
[Unit]
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
ExecStartPre=/usr/bin/test -f /opt/app/config.toml O After= ordena a inicialização; o Wants= faz o systemd de fato subir a dependência. O network-online.target só funciona se o serviço de espera de rede estiver habilitado na sua distro, então confira systemctl is-enabled NetworkManager-wait-online.service (ou o equivalente do systemd-networkd). O guarda ExecStartPre= é um jeito barato e honesto de falhar alto, com mensagem legível, em vez de um stack trace.
Uma segunda variante: o serviço inicia no boot mas morre na hora. Procure o que seu ambiente interativo tinha e o boot não tem — diferenças de PATH, um virtualenv, um HOME. Defina o que precisar explicitamente:
[Service]
Environment="PATH=/opt/app/venv/bin:/usr/bin"
User=appuser
WorkingDirectory=/opt/app Por que meu serviço não está logando no journal?
Se o journalctl -u não mostra nada, confira três coisas em ordem:
StandardOutput=eStandardError=na unidade — devem serjournal(o default) oujournal+console. Alguém pode tê-los posto emnull.- A app escreve num arquivo em vez do stdout. O systemd só captura stdout/stderr; quem escreve em arquivo de log passa longe do journal. Ou aponte a app para o stdout, ou leia o arquivo.
- Limites de armazenamento descartaram linhas antigas:
journalctl --disk-usage, eSystemMaxUse=em/etc/systemd/journald.confse o journal estiver sufocando.
Para depurar a inicialização em si, nada supera derrubar um shell dentro do contexto de boot:
[Service]
ExecStart=/bin/bash -c 'exec /opt/app/bin/server 2>&1' Ou, para mistérios de verdade, rode o comando exato do ExecStart= como o usuário da unidade, num shell — a maioria das diferenças de ambiente aparece nos primeiros dez segundos.
Como fazer ele reiniciar sozinho depois de um crash?
A política default é Restart=no: um serviço que crasha fica morto, e você fica sabendo por um usuário. Conserte isso por serviço:
[Service]
Restart=on-failure
RestartSec=5 | Configuração | Reinicia quando |
|---|---|
no (default) | Nunca |
on-failure | Saída não zero, sinal, timeout |
always | Qualquer saída, mesmo limpa |
on-watchdog | Só timeout do watchdog |
Pareie o Restart=on-failure com um guarda de taxa de start, para um loop de crashes não martelar a máquina: StartLimitIntervalSec= e StartLimitBurst= na seção [Unit]. Cinco falhas em sessenta segundos devem acordar um humano, não girar uma CPU.
Se você está montando isso como job agendado em vez de daemon, pese primeiro o cron vs timer do systemd — timers te dão logging no journal e ordenação de dependências de graça, que é exatamente o que este artigo passa o tempo todo buscando.
O checklist de 60 segundos
systemctl status <unit>— leia o estado e as últimas linhas.journalctl -u <unit> -n 50 --no-pager— ache o erro real.203/EXEC? Conserte caminho, shebang, permissões.Permission denied? Conserte usuário e dono dos arquivos.- Falha só no boot? Adicione
After=/Wants=network-online.targete um guardaExecStartPre=. - Mudou um unit file?
systemctl daemon-reload && systemctl restart <unit>. - Adicione
Restart=on-failurepara o próximo crash se anunciar em vez de se esconder.
A maioria dos momentos de “systemd é complicado” se resume a o erro esteve no journal o tempo todo. Leia-o antes de editar o unit file, não depois.
— mrsaynothing
Get the next one by email
One email per post. No spam, no algorithms.
what is this?Quantização GGUF: qual nível usar?
Gostou dos artigos? É assim que eu construo profissionalmente. me contrate