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.

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 que ExecStart lanç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. Requer PIDFile= 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 com RemainAfterExit=yes para o systemd continuar a mostrar o serviço como activo após terminar.
  • exec (systemd 239+) — como simple mas espera que o exec() 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.

  1. Saber verificar o estado de um serviço com systemctl status e distinguir active de enabled
  2. Iniciar, parar, reiniciar e recarregar um serviço com systemctl start/stop/restart/reload
  3. Activar e desactivar serviços do boot com systemctl enable/disable
  4. Criar um ficheiro de unidade .service em /etc/systemd/system/ com [Unit], [Service] e [Install]
  5. Sempre que se cria ou edita uma unidade, correr systemctl daemon-reload
  6. Criar um timer .timer com OnCalendar= e Persistent=true
  7. Consultar logs de um serviço com journalctl -u <servico> -f e filtrar por prioridade com -p err
  8. Ver e mudar o target padrão com systemctl get-default e systemctl set-default

Artigos Relacionados