Os systemd timers são a substituição moderna do cron em sistemas Linux. Eles oferecem tudo o que o cron faz — agendamento recorrente e de execução única — além de logging integrado, gerenciamento de dependências, controles de recursos e a capacidade de recuperar execuções perdidas. Se você gerencia servidores Linux, migrar do cron para systemd timers significa menos falhas silenciosas e muito mais visibilidade sobre o que suas tarefas agendadas estão fazendo.

Este guia orienta você na criação de timers do zero, na conversão de cron jobs existentes e no uso de recursos avançados como atrasos aleatórios e agendamento persistente.

Pré-requisitos

  • Um sistema Linux executando systemd (Ubuntu 16.04+, CentOS 7+, Debian 8+, ou qualquer distribuição moderna)
  • Acesso root ou sudo
  • Familiaridade básica com systemctl e arquivos de unit

Como os systemd Timers Funcionam

Um systemd timer consiste em dois arquivos de unit:

  1. Uma unit .service — define o que executar (o comando ou script propriamente dito)
  2. Uma unit .timer — define quando executar (o agendamento)

A unit de timer ativa sua unit de serviço correspondente no horário programado. Ambos os arquivos devem compartilhar o mesmo nome base (por exemplo, backup.service e backup.timer), a menos que você especifique explicitamente uma unit diferente com Unit=.

Essa separação é uma vantagem fundamental sobre o cron: a unit de serviço pode ser testada independentemente, reiniciada em caso de falha e inspecionada com as ferramentas padrão do systemd.

Criando Seu Primeiro Timer

Vamos criar um timer que executa um script de limpeza todos os dias às 2:00 da manhã.

Passo 1: Criar a Unit de Serviço

sudo nano /etc/systemd/system/daily-cleanup.service
[Unit]
Description=Daily temporary file cleanup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/cleanup.sh

O Type=oneshot informa ao systemd que este processo é executado até a conclusão e encerra — não é um daemon de longa execução. Este é o tipo correto para tarefas agendadas.

Crie o script referenciado:

sudo tee /usr/local/bin/cleanup.sh > /dev/null << 'EOF'
#!/bin/bash
find /tmp -type f -mtime +7 -delete
find /var/log -name "*.gz" -mtime +30 -delete
echo "Cleanup completed at $(date)"
EOF
sudo chmod +x /usr/local/bin/cleanup.sh

Passo 2: Criar a Unit de Timer

sudo nano /etc/systemd/system/daily-cleanup.timer
[Unit]
Description=Run daily cleanup at 2:00 AM

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true

[Install]
WantedBy=timers.target

Diretivas principais:

DiretivaFinalidade
OnCalendarAgendamento baseado em calendário (como o cron)
Persistent=trueExecuta imediatamente se uma execução agendada foi perdida (por exemplo, sistema desligado)
WantedBy=timers.targetNecessário para que o systemctl enable funcione

Passo 3: Habilitar e Iniciar

sudo systemctl daemon-reload
sudo systemctl enable --now daily-cleanup.timer

Passo 4: Verificar

systemctl list-timers daily-cleanup.timer

Saída:

NEXT                        LEFT          LAST PASSED UNIT                  ACTIVATES
Wed 2025-12-24 02:00:00 UTC 10h left      n/a  n/a   daily-cleanup.timer   daily-cleanup.service

Teste o serviço manualmente para confirmar que funciona:

sudo systemctl start daily-cleanup.service
journalctl -u daily-cleanup.service --no-pager -n 20

Entendendo a Sintaxe do OnCalendar

A diretiva OnCalendar usa um formato flexível de expressão de calendário:

DiaDaSemana Ano-Mês-Dia Hora:Minuto:Segundo

Aqui estão os padrões mais comuns:

AgendamentoExpressão OnCalendar
Todo dia à meia-noite*-*-* 00:00:00
Toda segunda às 9hMon *-*-* 09:00:00
Primeiro dia de cada mês*-*-01 00:00:00
A cada 15 minutos*-*-* *:00/15:00
Dias úteis às 18hMon..Fri *-*-* 18:00:00
Todo 1º de janeiro e julho*-01,07-01 00:00:00
A cada 2 horas*-*-* 00/2:00:00

Você pode validar expressões com systemd-analyze calendar:

systemd-analyze calendar "Mon..Fri *-*-* 09:00:00"
  Original form: Mon..Fri *-*-* 09:00:00
Normalized form: Mon..Fri *-*-* 09:00:00
    Next elapse: Mon 2025-12-29 09:00:00 UTC
       (in UTC): Mon 2025-12-29 09:00:00 UTC
       From now: 5 days left

Esta é uma das maiores vantagens sobre o cron — você pode verificar sua expressão de agendamento antes de implantá-la.

Timers Monotônicos: Intervalos Após Eventos

Nem todos os agendamentos são baseados no relógio. Timers monotônicos disparam em relação a um evento:

[Timer]
OnBootSec=5min
OnUnitActiveSec=1h
DiretivaDispara
OnBootSecX tempo após o boot
OnStartupSecX tempo após o systemd iniciar
OnUnitActiveSecX tempo após a última ativação do timer
OnUnitInactiveSecX tempo após o serviço terminar

Isso é útil para verificações de saúde, coleta de métricas ou qualquer tarefa que deve ser executada em intervalos independentemente do horário do relógio.

Exemplo — executar uma verificação de saúde a cada 5 minutos, começando 1 minuto após o boot:

[Unit]
Description=Periodic health check

[Timer]
OnBootSec=1min
OnUnitActiveSec=5min
AccuracySec=10s

[Install]
WantedBy=timers.target

O AccuracySec=10s reduz a janela de coalescência padrão de 1 minuto, fazendo com que seu timer dispare mais próximo de exatamente a cada 5 minutos.

Recursos Avançados

Atraso Aleatório

Evite que múltiplos servidores acessem o mesmo recurso simultaneamente:

[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=900

Isso agenda a tarefa entre 3:00 e 3:15 da manhã, com o systemd escolhendo um deslocamento aleatório para cada ciclo de boot.

Controles de Recursos

Como a tarefa é executada como um serviço systemd, você pode aplicar limites de recursos:

[Service]
Type=oneshot
ExecStart=/usr/local/bin/heavy-report.sh
CPUQuota=50%
MemoryMax=512M
IOWeight=50
Nice=15

O cron não tem equivalente — você precisaria adicionar chamadas nice, ionice e ulimit dentro de cada script.

Notificação de Falha

Dispare um alerta quando uma tarefa agendada falhar:

[Unit]
Description=Database backup
OnFailure=notify-admin@%n.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/db-backup.sh

Crie o template de notificação em /etc/systemd/system/notify-admin@.service:

[Unit]
Description=Send failure alert for %i

[Service]
Type=oneshot
ExecStart=/usr/local/bin/send-alert.sh "Unit %i failed on %H"

Convertendo Cron Jobs para systemd Timers

Aqui está uma abordagem sistemática para migrar cron jobs existentes.

Exemplo de entrada cron:

30 2 * * 1-5 /usr/local/bin/db-backup.sh

Isso executa o backup às 2:30 da manhã nos dias úteis. As units systemd equivalentes:

# /etc/systemd/system/db-backup.service
[Unit]
Description=Database backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/db-backup.sh
# /etc/systemd/system/db-backup.timer
[Unit]
Description=Database backup on weekday nights

[Timer]
OnCalendar=Mon..Fri *-*-* 02:30:00
Persistent=true

[Install]
WantedBy=timers.target

Referência rápida Cron para OnCalendar:

CronOnCalendar
0 * * * * (por hora)*-*-* *:00:00
*/5 * * * * (a cada 5 min)*-*-* *:00/5:00
0 0 * * 0 (semanal Dom)Sun *-*-* 00:00:00
0 0 1 * * (mensal)*-*-01 00:00:00
@rebootUse OnBootSec=0

Após criar as units:

sudo systemctl daemon-reload
sudo systemctl enable --now db-backup.timer

# Verificar
systemctl list-timers db-backup.timer

# Comentar a entrada antiga do cron
sudo crontab -e

Monitoramento e Depuração de Timers

Listar Todos os Timers Ativos

systemctl list-timers --all

Verificar Logs do Timer

journalctl -u daily-cleanup.service --since today
journalctl -u daily-cleanup.service --since "2025-12-20" --until "2025-12-23"

Depurar um Timer que Não Dispara

# Verificar status do timer
systemctl status daily-cleanup.timer

# Validar a expressão de calendário
systemd-analyze calendar "*-*-* 02:00:00"

# Verificar erros nos arquivos de unit
systemd-analyze verify /etc/systemd/system/daily-cleanup.*

# Ver todos os timers incluindo inativos
systemctl list-timers --all --no-pager

Timers Transientes para Tarefas Pontuais

Precisa executar algo uma vez em 30 minutos sem criar arquivos de unit?

systemd-run --on-active=30min /usr/local/bin/one-time-task.sh

Ou em um horário específico:

systemd-run --on-calendar="2025-12-24 15:00:00" /usr/local/bin/holiday-report.sh

Solução de Problemas

O timer mostra “n/a” em NEXT: O timer está carregado mas não habilitado. Execute systemctl enable --now <nome>.timer.

O serviço executa mas o timer não o dispara: Verifique se os nomes dos arquivos .timer e .service correspondem exatamente. Confira com systemctl cat <nome>.timer se há erros de digitação.

“Failed to parse calendar specification”: Sua sintaxe OnCalendar está incorreta. Valide com systemd-analyze calendar "sua expressão".

O timer dispara com atraso: O AccuracySec padrão é de 1 minuto. O systemd agrupa wake-ups para economizar energia. Defina AccuracySec=1s se precisar de precisão.

Execuções perdidas não são recuperadas: Você esqueceu Persistent=true na seção [Timer].

Resumo

  • Os systemd timers substituem o cron com dois arquivos de unit: um .service (o que executar) e um .timer (quando executar)
  • OnCalendar lida com agendamentos baseados no relógio; OnBootSec e OnUnitActiveSec lidam com intervalos
  • Persistent=true recupera execuções perdidas após períodos de inatividade
  • systemd-analyze calendar permite validar expressões de agendamento antes de implantá-las
  • O logging integrado via journalctl elimina a necessidade de redirecionamento manual de logs
  • Controles de recursos (CPUQuota, MemoryMax) e notificações de falha proporcionam confiabilidade de nível de produção
  • Todo cron job pode ser migrado para um systemd timer com mapeamento de sintaxe direto
  • Use systemd-run para tarefas agendadas pontuais e rápidas sem criar arquivos de unit

Configurar Cron Jobs no Linux | journalctl: Consultar e Analisar Logs do Sistema Linux | Gerenciando Serviços Linux com systemctl