Dia 7: Systemd no Linux — Services, Timers e journalctl
O systemd é o init system padrão em praticamente toda a distribuição Linux moderna — Ubuntu, Debian, RHEL, Rocky, Alma, Fedora, openSUSE, Arch. Quando o computador arranca, o systemd é o primeiro processo (PID 1) e lança tudo o resto: serviços de rede, logging, login, cron, mounts, sockets. Quem vem do Windows pode pensar nele como uma combinação de services.msc + Task Scheduler + Event Viewer num só sistema — mas integrado, declarativo e muito mais poderoso.
Este artigo cobre os quatro pilares do dia 7: services (unidades que corre daemons e processos), timers (agendamento que substitui o cron), targets (grupos de unidades que definem estados do sistema, como multi-utilizador ou gráfico) e journalctl (o sistema de logs centralizado que recolhe mensagens de todo o sistema num só sítio). No fim consegue criar um serviço personalizado, agendá-lo com um timer e diagnosticar problemas lendo logs estruturados — sem nunca abrir um ficheiro de texto plano em /var/log.
Neste artigo
O Que É o systemd e Porque Substituiu o SysVinit
Durante décadas, o Linux usou o SysVinit — o mesmo modelo do Unix System V dos anos 80. O SysVinit arrancava serviços em sequência usando scripts shell em /etc/init.d/, com níveis de execução (runlevels) 0 a 6. Funcionava, mas era lento (tudo em série, sem paralelismo), não supervisionava processos (se um daemon morresse, ficava morto) e não tinha forma nativa de lidar com dependências complexas.
O systemd (lançado em 2010 por Lennart Poettering e Kay Sievers) resolveu isto com uma arquitectura baseada em unidades (units) — ficheiros de configuração declarativos que descrevem serviços, mounts, sockets, timers, dispositivos e targets. O systemd arranca serviços em paralelo (usando sockets e D-Bus para resolver dependências dinamicamente), supervisiona processos (reinicia-os se morrerem), ger mounts e swap, mantém um registo de logs centralizado (o journal) e muito mais.
Nota: O systemd nem sempre foi consensual — distribuições como Devuan, Void Linux e Alpine usam alternativas (OpenRC, runit). Mas para administração de sistemas em ambientes corporativos (Ubuntu, RHEL, Debian), conhecer o systemd é essencial: é o padrão de facto desde 2015.
Os tipos de unidade mais relevantes para um administrador:
| Tipo | Extensão | Função |
|---|---|---|
service |
.service |
Daemons e processos (nginx, sshd, postgres) |
timer |
.timer |
Agendamento (substitui entradas cron) |
target |
.target |
Grupos de unidades (substitui runlevels) |
socket |
.socket |
Sockets IPC ou rede com activação sob pedido |
mount |
.mount |
Pontos de montagem (gerados a partir de /etc/fstab) |
slice |
.slice |
Grupos de cgroups para gestão de recursos |
As unidades vivem em três sítios, por ordem de precedência crescente: /usr/lib/systemd/system/ (fornecidas pelos pacotes — não editar), /run/systemd/system/ (runtime, volátil) e /etc/systemd/system/ (configuração do administrador — a que ganha). Em Debian/Ubuntu o caminho dos pacotes é /lib/systemd/system/ em vez de /usr/lib/systemd/system/, mas o /etc/systemd/system/ é universal.
systemctl — O Comando Central
O systemctl é a ferramenta de linha de comando que fala com o systemd. Com ele lista-se, inicia-se, para-se, reinicia-se, activa-se e desactiva-se unidades. É o comando que se usa 90% das vezes.
Estados possíveis de um serviço:
| Estado | Significado |
|---|---|
active (running) |
O processo está a correr |
active (exited) |
Correu e terminou com sucesso (script one-shot) |
inactive |
Parado, não está a correr |
failed |
O processo morreu ou saiu com código de erro |
activating |
Em transição — a iniciar |
Há ainda a distinção crucial entre estado corrente (está a correr agora?) e enabled/disabled (vai arrancar no boot?). Um serviço pode estar active mas disabled (foi iniciado manualmente, não arranca no boot) ou inactive mas enabled (vai arrancar no próximo boot, mas está parado agora).
Comandos essenciais do dia a dia:
# Ver o estado de um serviço
systemctl status nginx
# Ver apenas se está activo (código de saída 0 = activo)
systemctl is-active nginx
# Ver se está enabled (arranca no boot)
systemctl is-enabled nginx
# Iniciar, parar, reiniciar e recarregar configuração
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx
sudo systemctl reload nginx
# Activar para arrancar no boot
sudo systemctl enable nginx
# Activar E iniciar agora (atalho útil)
sudo systemctl enable --now nginx
# Desactivar do boot
sudo systemctl disable nginx
# Mascarar — impede arranque manual e automático (mais forte que disable)
sudo systemctl mask nginx
# Desmascarar
sudo systemctl unmask nginx
# Listar todas as unidades de tipo service
systemctl list-units --type=service
# Listar apenas as que falharam
systemctl --failed
# Listar todas as unidades instaladas (incluindo inactivas)
systemctl list-unit-files --type=service
Dica: reload vs restart: o reload pede ao daemon para reler a configuração sem interromper ligações activas (se o serviço suportar); o restart mata e reinicia o processo, cortando ligações. Em produção, sempre que possível, use reload.
Services — Unidades de Serviço
Uma unidade .service descreve como o systemd deve gerir um processo. O formato é INI-like, dividido em secções. As mais comuns são [Unit] (metadados e dependências), [Service] (como correr o processo) e [Install] (como integrar no boot).
Exemplo de um ficheiro de serviço real (nginx):
# /usr/lib/systemd/system/nginx.service (exemplo simplificado)
[Unit]
Description=The nginx HTTP and reverse proxy server
Documentation=https://nginx.org/en/docs/
After=network.target network-online.target remote-fs.target
Wants=network-online.target
[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/bin/rm -f /run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
ExecStop=/usr/sbin/nginx -s stop
TimeoutStopSec=5
KillMode=mixed
Restart=on-failure
[Install]
WantedBy=multi-user.target
Análise das directivas mais importantes:
| Directiva | Secção | Função |
|---|---|---|
Description |
Unit | Descrição legível (aparece no systemctl status) |
After= |
Unit | Arrancar depois destas unidades (ordem, não dependência forte) |
Requires= |
Unit | Dependência forte — se a dependência falhar, esta também falha |
Wants= |
Unit | Dependência fraca — tenta arrancar, mas não falha se não conseguir |
Type= |
Service | Tipo de processo: simple (padrão), forking, oneshot, exec |
ExecStart= |
Service | Comando para iniciar o serviço (caminho absoluto) |
ExecStop= |
Service | Comando para parar (se ausente, o systemd envia SIGTERM) |
Restart= |
Service | Quando reiniciar: no, on-failure, always, on-abnormal |
User= / Group= |
Service | Correr como este utilizador/grupo (em vez de root) |
WantedBy= |
Install | Em que target se integra quando se faz enable |
O campo Type= merece atenção. Os valores mais comuns:
simple(padrão) — o processo queExecStartlança é o serviço. O systemd considera-o activo imediatamente. Adequado para daemons que não fazem fork.forking— o processo inicial faz fork, o filho continua em background e o pai sai. Necessário para daemons tradicionais como nginx ou Apache prefork. RequerPIDFile=para o systemd saber qual é o processo real.oneshot— executa uma vez e termina. Ideal para scripts de setup, limpeza ou configuração. Pode combinar-se comRemainAfterExit=yespara o systemd continuar a mostrar o serviço como activo após terminar.exec(systemd 239+) — comosimplemas espera que oexec()tenha sucesso antes de considerar activo. Mais robusto.
Criar um Service Personalizado
Nada ensina melhor o systemd do que criar um serviço do zero. Vamos criar um serviço que corre um script de backup simples — um caso real que qualquer administrador precisa.
Primeiro, o script:
# /usr/local/bin/backup-dados.sh
#!/bin/bash
# Script de backup simples para systemd service
DESTINO="/srv/backup"
ORIGEM="/home"
DATA=$(date +%Y%m%d-%H%M%S)
logger "backup-dados: iniciado para $ORIGEM"
tar czf "$DESTINO/backup-$DATA.tar.gz" "$ORIGEM" 2>/dev/null
# Apagar backups com mais de 7 dias
find "$DESTINO" -name "backup-*.tar.gz" -mtime +7 -delete
logger "backup-dados: concluido com sucesso"
exit 0
Depois, o ficheiro de unidade. Em todas as distribuições, os serviços personalizados vão em /etc/systemd/system/:
# /etc/systemd/system/backup-dados.service
[Unit]
Description=Backup diario de dados
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-dados.sh
User=root
# Reiniciar em caso de falha
Restart=on-failure
RestartSec=30
[Install]
WantedBy=multi-user.target
Tornar o script executável, recarregar o systemd (para ler o novo ficheiro), activar e iniciar:
# Tornar o script executavel
sudo chmod +x /usr/local/bin/backup-dados.sh
# Recarregar a configuracao do systemd (sempre apos criar/editar units)
sudo systemctl daemon-reload
# Activar para arrancar no boot
sudo systemctl enable backup-dados.service
# Iniciar manualmente
sudo systemctl start backup-dados.service
# Ver o estado
systemctl status backup-dados.service
Atenção: daemon-reload é obrigatório sempre que se cria, edita ou apaga um ficheiro de unidade. Sem isto, o systemd continua a usar a definição antiga em memória. É o erro mais comum de quem começa com systemd.
Para editar um ficheiro de unidade sem decorar caminhos, o systemd oferece um atalho:
# Abrir o ficheiro no editor padrao (define EDITOR ou VISUAL)
sudo systemctl edit nginx.service
# Isto cria um override drop-in em:
# /etc/systemd/system/nginx.service.d/override.conf
# Apenas as directivas que se adicionam aqui sobrepoe-se as do pacote
# Editar o ficheiro completo (nao recomendado para unidades de pacote)
sudo systemctl edit --full nginx.service
O mecanismo de drop-in é a forma correcta de personalizar serviços de pacotes: em vez de editar o ficheiro original (que é substituído na próxima actualização do pacote), cria-se um override que só contém as alterações.
Timers — Substituir o Cron com Vantagem
O cron é o agendador clássico do Unix — funciona há 40 anos e continua a funcionar. Mas o systemd timers oferece vantagens reais: dependências explícitas (arrancar depois da rede), execução como utilizador específico sem sudo, logs integrados no journal (não dispersos em e-mails), execução precisa com precisão de segundos, e catch-up — se o sistema estava desligado na hora marcada, o timer executa ao arrancar.
Um timer precisa sempre de um service correspondente (com o mesmo nome base). Para o backup-dados.service que criámos acima, o timer seria:
# /etc/systemd/system/backup-dados.timer
[Unit]
Description=Timer para backup diario de dados
Requires=backup-dados.service
[Timer]
# Agendar: 02:30 todos os dias
OnCalendar=*-*-* 02:30:00
# Se o sistema estava desligado, executa ao arranque
Persistent=true
# Tolerancia de atraso (evita disparar exactamente ao segundo)
AccuracySec=1min
# Executar apenas uma vez por disparo
Unit=backup-dados.service
[Install]
WantedBy=timers.target
Activar o timer (não o service — o timer trata de o disparar):
# Recarregar o systemd
sudo systemctl daemon-reload
# Activar E iniciar o timer
sudo systemctl enable --now backup-dados.timer
# Ver todos os timers activos
systemctl list-timers
# Ver especificamente o nosso
systemctl status backup-dados.timer
Comparação dos tipos de agendamento:
| Directiva | Comportamento | Equivalente cron |
|---|---|---|
OnCalendar= |
Agenda em tempo absoluto (data/hora) | 30 2 * * * |
OnBootSec= |
X segundos após o arranque do sistema | @reboot (aproximado) |
OnUnitActiveSec= |
X segundos após a última execução | Não tem equivalente directo |
OnUnitInactiveSec= |
X segundos após o serviço terminar | Não tem equivalente directo |
Persistent=true |
Catch-up: executa se perdeu o horário (sistema desligado) | Não existe no cron |
A sintaxe OnCalendar usa o formato DiaSemana Ano-Mes-Dia Hora:Minuto:Segundo. Exemplos práticos:
# Todos os dias as 02:30
OnCalendar=*-*-* 02:30:00
# Segunda a sexta as 09:00
OnCalendar=Mon..Fri *-*-* 09:00:00
# A cada hora, no minuto 0
OnCalendar=*-*-* *:00:00
# A cada 15 minutos
OnCalendar=*:0/15
# Todos os domingos a meia-noite
OnCalendar=Sun *-*-* 00:00:00
# No dia 1 de cada mes
OnCalendar=*-*-01 00:00:00
# Validar a sintaxe sem activar o timer
systemd-analyze calendar "Mon..Fri *-*-* 09:00:00"
O systemd-analyze calendar é uma ferramenta útil que interpreta a expressão e mostra as próximas execuções previstas — vale a pena usar antes de activar um timer.
Nota: O cron não desapareceu — continua disponível em todas as distribuições e muitos administradores preferem-no pela simplicidade. O systemd timers complementa o cron, não o elimina. Para tarefas simples, o crontab -e continua a ser perfeitamente válido. O Dia 13 deste curso cobre o cron em profundidade e a comparação directa.
Targets — Runlevels Modernos
No SysVinit, o sistema tinha runlevels — níveis de execução numerados de 0 a 6, cada um representando um estado: 0 = desligar, 1 = single-user, 3 = multi-user sem gráfico, 5 = multi-user com gráfico, 6 = reboot. O systemd substituiu isto por targets, que são grupos de unidades que se activam em conjunto para atingir um determinado estado do sistema.
A correspondência entre runlevels antigos e targets modernos:
| Runlevel | Target | Significado |
|---|---|---|
| 0 | poweroff.target |
Desligar o sistema |
| 1 / s | rescue.target |
Modo de recuperação (single-user, root only) |
| 2 | multi-user.target |
Multi-utilizador sem ambiente gráfico (servidor típico) |
| 3 | multi-user.target |
Mesmo que 2 — multi-utilizador sem gráfico |
| 5 | graphical.target |
Multi-utilizador com ambiente gráfico (desktop) |
| 6 | reboot.target |
Reiniciar o sistema |
| — | emergency.target |
Modo de emergência (sistema de ficheiros raiz read-only) |
Comandos para gerir targets:
# Ver o target padrao (o que arranca por omissao)
systemctl get-default
# Saida tipica: graphical.target (desktop) ou multi-user.target (servidor)
# Mudar o target padrao (afeta o proximo boot)
sudo systemctl set-default multi-user.target
# Mudar para um target agora, sem reiniciar (ex: sair do modo grafico)
sudo systemctl isolate multi-user.target
# Arrancar em modo de recuperação a partir do GRUB:
# Adicionar no parametros do kernel: systemd.unit=rescue.target
# Ver todas as dependencias de um target
systemctl list-dependencies multi-user.target
Dica: Num servidor, o target padrão deve ser multi-user.target — não graphical.target. Ambiente gráfico num servidor consome RAM e CPU sem benefício, e amplia a superfície de ataque. Se a instalação ficou gráfica por engano, systemctl set-default multi-user.target resolve.
journalctl — Logs Centralizados
Antes do systemd, os logs do Linux viviam em ficheiros de texto plano em /var/log/ — /var/log/messages, /var/log/syslog, /var/log/auth.log — geridos pelo rsyslog ou syslog-ng. Funcionava, mas era difícil correlacionar eventos entre serviços, não havia metadados estruturados, e cada daemon escrevia para onde bem entendesse.
O systemd introduziu o journald — um serviço de logging centralizado que recolhe mensagens de todos os processos (via syslog, stdout/stderr de serviços, kernel, audit) num único repositório binário indexado. O journalctl é o comando para consultar este repositório.
Consultas essenciais:
# Todos os logs (do mais antigo ao mais recente)
journalctl
# Apenas os de hoje
journalctl --since today
# Apenas os da ultima hora
journalctl --since "1 hour ago"
# Intervalo especifico
journalctl --since "2026-07-21 09:00:00" --until "2026-07-21 12:00:00"
# Logs de um servico especifico (o mais usado no dia a dia)
journalctl -u nginx.service
# Logs de um servico, apenas as ultimas 50 linhas
journalctl -u nginx.service -n 50
# Seguir logs em tempo real (como tail -f)
journalctl -u nginx.service -f
# Logs de um PID especifico
journalctl _PID=1234
# Logs de um utilizador especifico
journalctl _UID=1000
# Logs do kernel (equivalente a dmesg)
journalctl -k
# Apenas erros e criticos
journalctl -p err
# Niveis de prioridade disponiveis:
# 0 emerg, 1 alert, 2 crit, 3 err, 4 warning, 5 notice, 6 info, 7 debug
journalctl -p warning
# Logs entre duas prioridades
journalctl -p err..alert
# Formato JSON (para processar com jq)
journalctl -u nginx.service -o json
# Ver os campos estruturados de uma entrada
journalctl -o verbose -n 1
Filtrar por prioridade é uma das funções mais úteis. A tabela de níveis de syslog:
| Código | Nome | Quando usar |
|---|---|---|
| 0 | emerg |
Sistema inutilizável |
| 1 | alert |
Acção imediata necessária |
| 2 | crit |
Condições críticas |
| 3 | err |
Erros de runtime |
| 4 | warning |
Avisos — não é erro mas merece atenção |
| 5 | notice |
Eventos normais mas significativos |
| 6 | info |
Mensagens informativas |
| 7 | debug |
Informação de debug (muito verboso) |
Por defeito, o journal é volátil em algumas distribuições (apaga-se ao reiniciar) ou persistente noutras. Para garantir persistência (logs sobrevivem a reboot):
# Criar o directorio de journal persistente (todas as distros)
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
# Ou editar /etc/systemd/journald.conf:
# [Journal]
# Storage=persistent
# SystemMaxUse=500M # limite de disco
# MaxRetentionSec=1month # retencao temporal
# Reiniciar o journald para aplicar
sudo systemctl restart systemd-journald
# Ver o consumo actual do journal
journalctl --disk-usage
# Forcar rotacao (criar novo ficheiro de journal)
sudo journalctl --rotate
# Apagar logs com mais de 2 semanas
sudo journalctl --vacuum-time=2weeks
# Apagar logs ate o journal ocupar no maximo 100M
sudo journalctl --vacuum-size=100M
O journal é armazenado em formato binário indexado em /var/log/journal/ (persistente) ou /run/log/journal/ (volátil). Não é texto — é estruturado, com campos como _PID, _UID, _SYSTEMD_UNIT, _COMM, PRIORITY que permitem filtragem precisa.
O rsyslog continua a correr em paralelo com o journald na maioria das distribuições — lê do journal e escreve ficheiros de texto plano para compatibilidade com ferramentas tradicionais. Ambos coexistem sem conflito.
Erros Comuns
| Problema | Causa | Solução |
|---|---|---|
Failed to execute /usr/local/bin/script.sh: Exec format error |
Falta o shebang (#!/bin/bash) no início do script ou o script não é executável |
Adicionar #!/bin/bash na primeira linha; chmod +x script.sh |
| Serviço modificado mas o systemd ignora as alterações | Faltou fazer daemon-reload após editar o ficheiro de unidade |
sudo systemctl daemon-reload e depois restart do serviço |
status=203/EXEC no systemctl status |
O caminho em ExecStart= está errado ou o binário não existe |
Verificar com which comando ou usar caminho absoluto; confirmar que o binário está instalado |
Service em loop de reinício constante (restart repetido) |
Restart=always com um serviço que falha imediatamente |
Adicionar StartLimitIntervalSec= e StartLimitBurst= para limitar tentativas; corrigir a causa do crash |
journalctl mostra logs que desaparecem após reboot |
Journal está em modo volátil (Storage=auto sem /var/log/journal/) |
Criar /var/log/journal/ ou definir Storage=persistent em /etc/systemd/journald.conf |
Timer não dispara apesar de estar enabled |
O timer está activado mas o service associado não existe ou tem nome diferente | O timer e o service têm de partilhar o mesmo nome base; verificar com systemctl list-timers --all |
Sistema arranca em emergency.target |
Erro crítico no arranque — filesystem não montou, unidade com erro de sintaxe | No prompt de emergência: journalctl -xb para ver logs do boot; systemctl default para tentar continuar |
Failed to start nginx.service: Unit not found |
O pacote não está instalado ou o nome da unidade está errado | Instalar o pacote (apt install nginx / dnf install nginx); confirmar nome com systemctl list-unit-files | grep nginx |
Checklist Rápido
Antes de avançar para o Dia 8, confirme que domina os pontos seguintes. Cada item tem um link para a secção relevante do artigo.
- Saber verificar o estado de um serviço com
systemctl statuse distinguiractivedeenabled - Iniciar, parar, reiniciar e recarregar um serviço com
systemctl start/stop/restart/reload - Activar e desactivar serviços do boot com
systemctl enable/disable - Criar um ficheiro de unidade
.serviceem/etc/systemd/system/com[Unit],[Service]e[Install] - Sempre que se cria ou edita uma unidade, correr
systemctl daemon-reload - Criar um timer
.timercomOnCalendar=ePersistent=true - Consultar logs de um serviço com
journalctl -u <servico> -fe filtrar por prioridade com-p err - Ver e mudar o target padrão com
systemctl get-defaultesystemctl set-default
Artigos Relacionados
- Dia 1: Terminal, Shell e Comandos Essenciais — base de linha de comando que este artigo assume
- Dia 2: Sistema de Ficheiros Linux — FHS, Mounts e Links — o systemd gere mounts automaticamente a partir do fstab
- Manual oficial do systemctl — referência completa de todos os subcomandos (freedesktop.org)
- Manual oficial do systemd.service — todas as directivas de unidades de serviço
- Manual oficial do systemd.timer — sintaxe completa de agendamento
- Manual oficial do journalctl — todas as opções de filtragem e formatação
- Arch Wiki: systemd — guia prático excelente, aplicável a qualquer distribuição
- Arch Wiki: systemd/Timers — exemplos práticos de timers com sintaxe OnCalendar
- Arch Wiki: journald — configuração e manutenção do journal
- systemd.io — site oficial do projecto systemd
- Sistemas Operativos no kbase.pt — mais artigos Linux e administração de sistemas