Tutoriais de VPS
O que fazer quando um serviço systemd falha ao iniciar em um VPS (2026): systemctl status, journalctl -u e diagnóstico do arquivo Unit
Quando um serviço em um VPS falha ao executar systemctl start, mostra Active como failed ou reinicia automaticamente sem parar, as causas comuns incluem erros de sintaxe no arquivo Unit, caminho errado no ExecStart, WorkingDirectory inexistente, falha ao ler o EnvironmentFile, permissões insuficientes do usuário de execução, porta ocupada, serviços de dependência não iniciados ou StartLimitHit. Este artigo diagnostica camada por camada usando systemctl status, journalctl -u, systemctl cat, daemon-reload, permissões e loops de reinício.

Quando um serviço falha ao iniciar em um VPS, muita gente só repete:
sudo systemctl restart app
Se o problema é que a configuração não foi carregada, um caminho está errado, as permissões são insuficientes ou a porta está ocupada, reiniciar cem vezes não resolve. Pior ainda, se o Unit estiver configurado com Restart=always, o serviço pode entrar em um loop de reinício, inundar os logs e sobrecarregar CPU e disco.
Este artigo trata especificamente de como investigar um serviço systemd que falha ao iniciar. Não é um diagnóstico exclusivo de um software como Nginx, MySQL ou Redis, mas sim o problema básico que todo serviço auto-hospedado enfrenta: como ler systemctl status, quais linhas olhar em journalctl -u, como confirmar o arquivo Unit, quando é necessário daemon-reload e como lidar com Start request repeated too quickly.
Primeiro veja o status do serviço:
sudo systemctl status app --no-pager
Substitua app pelo nome do seu serviço, por exemplo:
sudo systemctl status nginx --no-pager
sudo systemctl status docker --no-pager
sudo systemctl status myapp --no-pager
Preste atenção nestes campos:
Loaded:se o arquivo Unit existe e se está enabled;Active:se éactive,failed,activatingouauto-restart;Main PID:se o processo principal ainda está ativo;ExecStart=se o comando depois dele realmente foi executado;code=exited, status=...qual é o código de saída;- se as últimas linhas do log contêm um erro claro.
Se você só vê um trecho pequeno de log, não é suficiente. Em seguida, veja o journal completo.
O comando mais comum:
sudo journalctl -u app -n 120 --no-pager
Para ver a última hora:
sudo journalctl -u app --since "1 hour ago" --no-pager
Para acompanhar em tempo real:
sudo journalctl -u app -f
Se acabou de falhar ao iniciar, você pode reproduzir e ver o log imediatamente assim:
sudo systemctl restart app
sudo journalctl -u app -n 120 --no-pager
Não olhe só a última linha. A causa real costuma estar antes, por exemplo:
No such file or directory
Permission denied
Address already in use
Failed at step USER
Failed to load environment files
Working directory ... does not exist
Start request repeated too quickly
Se você ainda não está familiarizado com o journalctl, pode ler primeiro este artigo: Como ler os logs de um VPS (journalctl, systemctl, Nginx, logs de aplicação).
Muita gente edita o arquivo errado.
Primeiro veja o conteúdo do Unit que o systemd realmente enxerga:
sudo systemctl cat app
Ele mostra o arquivo Unit principal e as configurações de override drop-in, por exemplo:
# /etc/systemd/system/app.service
[Service]
WorkingDirectory=/var/www/app
ExecStart=/usr/bin/node server.js
# /etc/systemd/system/app.service.d/override.conf
[Service]
Environment=NODE_ENV=production
Se você claramente alterou /etc/systemd/system/app.service mas systemctl cat app não mostra mudanças, normalmente é porque esqueceu:
sudo systemctl daemon-reload
Ordem recomendada após alterar um arquivo Unit:
sudo systemctl daemon-reload
sudo systemctl restart app
sudo systemctl status app --no-pager
Alterar apenas o arquivo de configuração da aplicação não necessariamente exige daemon-reload; alterar .service, .timer ou um override drop-in normalmente exige.
No Unit, ExecStart é melhor escrever como caminho absoluto:
[Service]
ExecStart=/usr/bin/node /var/www/app/server.js
Não dependa do PATH, nvm, pyenv ou conda do seu shell de login. Quando o systemd inicia um serviço, o ambiente é diferente do que você tem após entrar via SSH.
Primeiro verifique o caminho do comando:
which node
which python3
which php
which gunicorn
Se você instalou o Node com nvm, which node pode estar no diretório home do usuário:
/home/deploy/.nvm/versions/node/v20.11.1/bin/node
Esse tipo de caminho também funciona no systemd, mas você precisa confirmar que o usuário de execução tem permissão e não presumir que .bashrc será carregado. Uma abordagem mais robusta é fixar o ambiente de execução em um caminho explícito ou escrever um script wrapper.
Muitas aplicações iniciam bem manualmente mas falham no systemd porque o diretório de trabalho é diferente.
Configuração comum:
[Service]
WorkingDirectory=/var/www/app
ExecStart=/usr/bin/npm start
Verifique o diretório:
ls -ld /var/www/app
Se no log aparecer:
CHDIR spawning ... No such file or directory
Working directory ... does not exist
significa que o caminho de WorkingDirectory está errado ou que o diretório foi removido.
Se o diretório existe mas o usuário do serviço não consegue entrar, também vai falhar. Por exemplo, se o serviço usa User=www-data, teste com esse usuário:
sudo -u www-data test -x /var/www/app
sudo -u www-data ls /var/www/app
Configuração comum do Unit:
[Service]
User=deploy
Group=deploy
Se no log aparecer:
Failed at step USER spawning ...: No such process
significa que o usuário não existe ou que o systemd não consegue alternar para esse usuário.
Verifique o usuário:
id deploy
Se for um problema de permissão de arquivo, o log pode ser:
Permission denied
Execute manualmente o comando de inicialização com o usuário de destino:
sudo -u deploy /usr/bin/node /var/www/app/server.js
Isso é mais informativo do que executar manualmente como root. O fato de o root conseguir executar não significa que deploy, www-data, postgres ou redis consigam.
Muitos serviços são configurados assim:
[Service]
EnvironmentFile=/etc/app/app.env
ExecStart=/usr/bin/node /var/www/app/server.js
Se o arquivo não existir, o log pode mostrar:
Failed to load environment files: No such file or directory
Verifique:
sudo ls -l /etc/app/app.env
sudo systemctl cat app
Se esse arquivo de ambiente não for obrigatório, você pode adicionar - para que ele não gere um erro grave quando não existir:
EnvironmentFile=-/etc/app/app.env
Mas não adicione - só para "conseguir iniciar". Se a aplicação depende do endereço do banco de dados, de segredos ou da configuração de porta, a ausência do arquivo de ambiente só vai tornar o erro mais oculto.
Também preste atenção ao formato do arquivo de ambiente: ele não é um script shell completo. Formato comum:
NODE_ENV=production
PORT=3000
DATABASE_URL=postgres://user:[email protected]:5432/app
Não escreva lógica shell complexa dentro dele.
Se no log aparecer:
Address already in use
EADDRINUSE
bind: address already in use
primeiro verifique quem está ocupando a porta:
sudo ss -lntup
Para ver apenas uma porta específica:
sudo ss -lntup | grep ':3000'
Causas comuns:
- Um processo antigo não encerrou;
- O mesmo serviço foi iniciado duas vezes;
- Um contêiner Docker está ocupando a porta;
- Nginx / Caddy / Apache está disputando a 80/443 com a aplicação;
- Vários serviços na configuração usam a mesma porta.
Não faça kill -9 diretamente. Primeiro confirme a origem do processo:
ps -fp PID
systemctl status PID对应服务
Se um problema de porta causar 502 no site, você pode ler: O que fazer com 502 / 504 Bad Gateway em um VPS.
Comum nos Units:
[Unit]
After=network.target postgresql.service
Requires=postgresql.service
Isso só garante a ordem e as relações de dependência; não significa que o PostgreSQL já consegue aceitar conexões, nem que um banco de dados remoto na rede está acessível.
Se o log da aplicação mostrar:
connection refused
connection timed out
could not connect to server
verifique também os serviços de dependência:
sudo systemctl status postgresql --no-pager
sudo ss -lntup | grep ':5432'
Se for Docker, Redis, MySQL ou uma API remota, também é preciso confirmar a acessibilidade separadamente. Não atribua todos os problemas de dependência ao systemd.
Se você vir:
Start request repeated too quickly
Failed with result 'start-limit-hit'
significa que o serviço falhou muitas vezes em pouco tempo e o systemd pausou a inicialização dele.
Primeiro veja o motivo da falha:
sudo journalctl -u app -n 200 --no-pager
Após corrigir a causa raiz, redefina o estado de falha:
sudo systemctl reset-failed app
sudo systemctl start app
Se o Unit contiver:
[Service]
Restart=always
RestartSec=1
o serviço vai reiniciar com muita frequência. Em produção, você pode aumentar adequadamente:
[Service]
Restart=on-failure
RestartSec=5s
Não use uma política de reinício mais agressiva para mascarar falhas de inicialização. Isso só deixa os logs mais confusos.
Tipos comuns:
Type=simple
Type=exec
Type=forking
Type=oneshot
Para a maioria das aplicações que rodam em primeiro plano, simple ou exec é suficiente. Não escreva um processo em primeiro plano como forking.
Se o serviço sai imediatamente após iniciar mas é uma tarefa de execução única, você deve considerar:
[Service]
Type=oneshot
RemainAfterExit=yes
Se for um serviço de longa duração, a saída do processo significa que a aplicação em si terminou; olhe os logs da aplicação em vez de disfarçar isso como sucesso.
Recomenda-se seguir esta ordem sempre que alterar um Unit do systemd:
sudo systemd-analyze verify /etc/systemd/system/app.service
sudo systemctl daemon-reload
sudo systemctl restart app
sudo systemctl status app --no-pager
sudo journalctl -u app -n 80 --no-pager
systemd-analyze verify consegue detectar antecipadamente alguns problemas de sintaxe e configuração do Unit. Ele não garante que a aplicação vai iniciar, mas evita erros básicos de Unit.
Se o serviço em produção for importante, faça backup antes de alterar:
sudo cp /etc/systemd/system/app.service /etc/systemd/system/app.service.bak.$(date +%F-%H%M)
Para reverter:
sudo cp /etc/systemd/system/app.service.bak.时间戳 /etc/systemd/system/app.service
sudo systemctl daemon-reload
sudo systemctl restart app
Quando systemctl start ou restart falhar, verifique nesta ordem:
systemctl status app --no-pagerpara ver Active, código de saída e os últimos logs.journalctl -u app -n 120 --no-pagerpara ver o motivo completo da falha.systemctl cat apppara confirmar o Unit e o override realmente carregados.- Após alterar o Unit, execute
systemctl daemon-reload. - Verifique se
ExecStarté um caminho absoluto e se o comando existe. - Verifique se
WorkingDirectoryexiste e se o usuário do serviço consegue acessá-lo. - Verifique se
User/Groupexistem e se as permissões estão corretas. - Verifique se
EnvironmentFileexiste e se o formato está correto. - Use
ss -lntuppara verificar se a porta está ocupada. - Verifique se os serviços de dependência estão iniciados e realmente acessíveis.
- Ao encontrar
start-limit-hit, corrija primeiro a causa raiz e depoissystemctl reset-failed app. - Faça backup do Unit antes de modificar e use
systemd-analyze verifypara uma verificação básica depois.
Quando um serviço systemd falha ao iniciar, não confie em reinícios repetidos e sorte. Primeiro use systemctl status para determinar o estado, depois use journalctl -u para encontrar o primeiro erro real e em seguida veja systemctl cat para confirmar se o Unit está sendo carregado como você espera.
A maioria dos problemas acaba em algumas categorias: caminho errado, permissões insuficientes, arquivo de ambiente ausente, conflito de porta, dependência não iniciada, Unit alterado mas sem daemon-reload, ou serviço que entrou em StartLimitHit. Diagnosticar em ordem é muito mais rápido do que alterar a configuração às cegas, e não deixa a situação mais confusa quanto mais você mexe.