Nesse artigo você vai aprender como usar systemd timer em vez de cron para o agendamento de tarefas.
Pense no “Systemd Timer” como a versão moderna e mais inteligente do cron. Em sistemas Linux atuais (como Debian), o systemd já gerencia serviços do sistema e os timers são uma funcionalidade nativa dele.
Por que usar Systemd Timers?
- Ambiente controlado: o serviço executado pelo timer roda em um ambiente previsível, muito mais próximo de um shell de login do que o ambiente minimalista do cron.
- Logs superiores: toda a saída da execução é capturada pelo
journald
e pode ser inspecionada comjournalctl
, facilitando a depuração. - Gerenciamento como serviço: sua tarefa vira um “serviço” do sistema; você pode iniciar, parar e consultar status com o
systemctl
.
Visão geral
Implementaremos em duas partes: primeiro criamos um serviço que executa o script, depois um timer que agenda quando esse serviço deve rodar.
Vou usar o agendamento de geração de relatórios para o GoAccess como exemplo.
Passo 0 (opcional): Remover o trabalho do cron antigo
Para evitar execuções duplicadas, desative/remova a entrada antiga do cron (ajuste o caminho conforme seu ambiente):
sudo rm /etc/cron.d/update-goaccess.sh
Passo 1: Criar o arquivo de serviço do systemd
Crie o arquivo de unidade que descreve a tarefa a ser executada:
sudo vim /etc/systemd/system/goaccess-reporter.service
Conteúdo do arquivo:
[Unit]
Description=Gera relatórios periódicos do GoAccess para logs do Nginx
[Service]
Type=oneshot
ExecStart=/usr/local/bin/update-goaccess.sh
Explicações:
Description
: descrição do que o serviço faz.Type=oneshot
: apropriado para scripts que executam uma tarefa e encerram.ExecStart
: comando (seu script) a ser executado.
Passo 2: Criar o arquivo de timer do systemd
Agora crie o “agendador” que dispara o serviço acima:
sudo vim /etc/systemd/system/goaccess-reporter.timer
Conteúdo do arquivo:
[Unit]
Description=Executa o goaccess-reporter.service a cada 2 minutos
[Timer]
OnBootSec=5min
OnCalendar=*:0/2
Persistent=true
[Install]
WantedBy=timers.target
Explicações:
OnBootSec=5min
: primeira execução 5 minutos após a inicialização.OnCalendar=*:0/2
: equivalente a*/2 * * * *
no cron (a cada 2 minutos).Persistent=true
: se o host estava desligado no horário programado, executa ao ligar.WantedBy=timers.target
: permite habilitar o timer na inicialização do sistema.
Passo 3: Recarregar, habilitar e iniciar
Carregue as unidades recém-criadas e inicie o agendamento:
sudo systemctl daemon-reload
sudo systemctl enable goaccess-reporter.timer
sudo systemctl start goaccess-reporter.timer
Como verificar se está funcionando
Listar timers (veja NEXT e LAST para confirmar o agendamento):
systemctl list-timers
Ver logs do serviço (saída das execuções do script):
journalctl -u goaccess-reporter.service
Acompanhar em tempo real:
journalctl -u goaccess-reporter.service -f
Conclusão
Os systemd timers são a forma moderna e robusta de agendar tarefas, oferecendo um ambiente mais previsível e logs integrados que eliminam os problemas típicos do cron.
Possíveis melhorias: Timer “definitivo” com nice/ionice
Após validar o funcionamento, você pode endurecer a configuração para a execução diária “oficial” com baixa prioridade de CPU e I/O, além de definir a LC_ALL
explicitamente. Abaixo estão as unidades de exemplo e, em seguida, um resumo das diferenças em relação ao agendamento de teste (a cada 2 minutos).
Unidade de serviço com prioridades ajustadas
/etc/systemd/system/goaccess-reporter.service
[Unit]
Description=Gera relatórios periódicos do GoAccess para logs do Nginx
[Service]
Type=oneshot
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7
Environment=LC_ALL=C.UTF-8
ExecStart=/usr/local/bin/update-goaccess.sh
Unidade de timer diária (03:17)
/etc/systemd/system/goaccess-reporter.timer
[Unit]
Description=Agendamento do goaccess-reporter.service
[Timer]
OnCalendar=*-*-* 03:17:00
Persistent=true
[Install]
WantedBy=timers.target
Aplicar e verificar
sudo systemctl daemon-reload
sudo systemctl enable --now goaccess-reporter.timer
sudo systemctl list-timers goaccess-reporter.timer
Diferenças em relação ao agendamento de teste
- Frequência: o exemplo inicial usa
OnCalendar=*:0/2
(a cada 2 minutos) para testes; a versão “definitiva” agenda uma execução diária explícita às 03:17 (OnCalendar=*-*-* 03:17:00
). - Prioridades: adiciona
Nice=10
(reduz prioridade de CPU) eIOSchedulingClass=best-effort
/IOSchedulingPriority=7
(reduz prioridade de I/O), minimizando impacto em horários de backup ou maior carga. - Locale: define
Environment=LC_ALL=C.UTF-8
para padronizar mensagens/ordenção e evitar variações de ambiente. - Ativação: usa
enable --now
para habilitar e iniciar no mesmo comando; funcionalmente equivalente a habilitar e iniciar em comandos separados. - OnBootSec: omitido na versão diária; você pode mantê-lo se desejar uma primeira execução “após X minutos” no boot, além do agendamento diário.
Entendendo OnCalendar=
A diretiva OnCalendar=
define quando o timer dispara o serviço. Ela usa a sintaxe de “eventos de calendário” do systemd, que aceita curingas, listas, faixas e passos. Você pode escrever tanto palavras-chave (p.ex., daily
, weekly
) quanto formatos explícitos de data/hora.
Formatos básicos
- Palavras-chave:
minutely
,hourly
,daily
,weekly
,monthly
,yearly
(ouannually
). - Forma explícita:
AAAA-MM-DD HH:MM:SS
, com curingas*
e listas/ranges.
Exemplo:*-*-* 03:17:00
→ todos os dias às 03:17.
Listas, faixas e passos
- Lista:
Mon,Wed,Fri 08:00
→ seg/qua/sex às 08:00. - Faixa:
Mon..Fri 19:30
→ de segunda a sexta às 19:30. - Passo:
*:0/15
→ a cada 15 minutos (00, 15, 30, 45).
Exemplos comuns
# A cada 2 minutos (equivalente a */2 * * * * no cron)
OnCalendar=*:0/2
# Todo dia às 03:17
OnCalendar=*-*-* 03:17:00
# Somente dias úteis às 19:30
OnCalendar=Mon..Fri 19:30
# Toda segunda-feira às 03:00
OnCalendar=Mon 03:00
# No primeiro dia de todo mês às 00:15
OnCalendar=*-*-01 00:15:00
# A cada 15 minutos, todos os dias
OnCalendar=*:0/15
Dicas práticas
- Hora local: por padrão, o agendamento usa a hora local do sistema.
- Testar com list-timers: após alterar o timer, rode
systemctl daemon-reload
e confirasystemctl list-timers
para verificar os próximos disparos. - Combinar com OnBootSec: você pode usar
OnBootSec=
para garantir uma primeira execução após o boot, além doOnCalendar=
recorrente.