Idioma: Português (BR)
note · scheduling
Pushkar Kumar 7 min de leitura
Migrando do cron para os timers do systemd
Converta entradas de crontab em units .timer e .service do systemd: sintaxe OnCalendar, logging, atrasos randomizados, execuções de recuperação e pegadinhas.
- systemd
- cron
- linux
Nesta página
O cron tem comandado os jobs agendados do mundo há quarenta anos e, na maioria dos servidores, ainda funciona bem. Mas no momento em que um job precisa de logging estruturado, um ambiente controlado, ordenação de dependências ou uma forma de se recuperar depois que a máquina ficou desligada, o modelo do cron começa a ranger. É aí que entram os timers do systemd — e se a sua distribuição já roda systemd (Debian, Ubuntu, RHEL, Fedora, Arch e SUSE todas rodam), você tem um agendador mais capaz parado sem uso.
Este post percorre o que realmente muda quando você migra, com units reais que você pode adaptar.
Por que se dar ao trabalho de sair do cron
O cron é uma única linha. Essa brevidade é o seu atrativo e o seu limite:
- Logging. A saída de um job cron vai para onde você redirecionar e, se você esquecer, ela é enviada por e-mail para uma caixa postal que ninguém lê. Um service do systemd escreve no journal automaticamente —
journalctl -u myjob.servicemostra cada execução, com timestamps e códigos de saída. - Ambiente. O cron roda com um
PATHdeliberadamente mínimo e quase nenhum ambiente, o que é a clássica armadilha do “funciona no meu shell, falha no cron”. Uma unit de service declara o seu ambiente explicitamente. - Execuções perdidas. Se o host estiver suspenso ou desligado no minuto agendado, o cron simplesmente pula o job. Um timer com
Persistent=trueo executa assim que a máquina volta. - Sobreposição e recursos. O systemd não inicia uma segunda cópia de um job enquanto a primeira ainda está rodando, e você pode anexar
CPUQuota=,MemoryMax=e outros controles de recursos a uma unit.
Você não precisa migrar tudo. Mas para jobs em que uma falha silenciosa lhe custa caro, os timers valem os dois arquivos que exigem.
O modelo de dois arquivos
Uma linha do cron faz agendamento e execução em um só lugar. O systemd divide isso em um service (o que executar) e um timer (quando executar). Eles compartilham um nome base.
Pegue esta entrada de crontab — rodar um script de backup todo dia às 02:30:
30 2 * * * /usr/local/bin/backup.shIsso se torna duas units em /etc/systemd/system/.
O service, backup.service:
[Unit]
Description=Nightly backup
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.shO timer, backup.timer:
[Unit]
Description=Run nightly backup at 02:30
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
[Install]
WantedBy=timers.targetType=oneshot diz ao systemd que se espera que o job execute, termine e saia — o tipo certo para quase toda tarefa no estilo cron. A seção [Install] do timer é o que faz o systemctl enable funcionar; sem WantedBy=timers.target, o timer não será armado no boot.
Habilite e inicie:
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timerNote que você habilita o timer, não o service. O timer aciona o service quando dispara.
Traduzindo o agendamento: OnCalendar
A parte mais difícil da migração é o campo de agendamento, porque o systemd usa OnCalendar= em vez dos cinco campos do cron. O formato é DOW YYYY-MM-DD HH:MM:SS, e ele é genuinamente mais legível depois que você aprende. Alguns mapeamentos comuns:
# cron # OnCalendar
*/15 * * * * *-*-* *:0/15:00 (every 15 minutes)
0 * * * * *-*-* *:00:00 (hourly, on the hour)
30 2 * * * *-*-* 02:30:00 (daily at 02:30)
0 4 * * 1 Mon *-*-* 04:00:00 (Mondays at 04:00)
0 0 1 * * *-*-01 00:00:00 (1st of the month)
0 9 * * 1-5 Mon..Fri *-*-* 09:00:00 (weekdays at 09:00)Também existem atalhos convenientes — hourly, daily, weekly, monthly — então OnCalendar=daily é equivalente à meia-noite todos os dias. O comando mais útil de todos durante a migração é o systemd-analyze calendar, que faz o parse de uma expressão e mostra os próximos horários de disparo:
$ systemd-analyze calendar --iterations=3 'Mon..Fri *-*-* 09:00:00'
Original form: Mon..Fri *-*-* 09:00:00
Normalized form: Mon..Fri *-*-* 09:00:00
Next elapse: Mon 2026-06-08 09:00:00 UTC
From now: 4h 12min left
(next 3) Tue 2026-06-09 09:00:00 UTC
Wed 2026-06-10 09:00:00 UTCSe essa saída corresponder ao que a sua linha do cron fazia, o agendamento está correto. Se não corresponder, você pegou o bug antes que ele fosse para produção.
Convertendo cron para systemd: os agendamentos que não mapeiam um para um
A tabela acima cobre campos numéricos simples. Três tipos de linha de cron exigem mais cuidado, porque uma conversão literal roda em outro dia ou nunca roda.
Os atalhos @. O systemd tem seus próprios agendamentos nomeados, e eles concordam com os do cron na maior parte. Um não concorda:
# cron # systemd
@hourly OnCalendar=hourly (*-*-* *:00:00)
@daily OnCalendar=daily (*-*-* 00:00:00)
@weekly OnCalendar=weekly (Mon *-*-* 00:00:00, cron uses Sunday)
@monthly OnCalendar=monthly (*-*-01 00:00:00)
@yearly OnCalendar=yearly (*-01-01 00:00:00)
@reboot OnBootSec=1min (a monotonic timer, not OnCalendar)O @weekly do cron é 0 0 * * 0, meia-noite de domingo. O weekly do systemd é meia-noite de segunda-feira. Se um relatório precisa estar pronto antes da manhã de segunda, escreva OnCalendar=Sun *-*-* 00:00:00 em vez de confiar no nome. O @reboot não tem forma de calendário: use OnBootSec= no timer, ou dispense o timer e dê ao serviço WantedBy=multi-user.target.
Dia do mês e dia da semana juntos. Quando os dois campos estão restritos, o cron executa o job se qualquer um deles corresponder. 0 9 1 * 1 dispara no dia 1 de todo mês e em toda segunda-feira. OnCalendar=Mon *-*-01 09:00:00 só dispara quando o dia 1 cai numa segunda, poucas vezes por ano. Para manter o comportamento do cron, liste as duas expressões; um timer aceita várias linhas OnCalendar= e dispara em qualquer uma delas:
[Timer]
OnCalendar=*-*-01 09:00:00
OnCalendar=Mon *-*-* 09:00:00Passos com deslocamento e domingo como 7. 5-59/10 * * * * começa no minuto 5, o que vira *:05/10. O cron aceita tanto 0 quanto 7 para domingo; o systemd quer o nome, Sun. Passe cada linha convertida pelo systemd-analyze calendar antes de habilitá-la. Essa verificação leva segundos, e é assim que você pega os atalhos que parecem equivalentes, mas não são.
As pegadinhas que realmente mordem
Fuso horário. O cron usa a hora local do sistema. Os timers do systemd também usam por padrão, mas o OnCalendar é avaliado no fuso horário do timer, o que pode surpreender você em servidores configurados para UTC. Fixe-o explicitamente com OnCalendar=Mon *-*-* 04:00:00 America/New_York se a hora local importar, e lembre-se de que as transições de horário de verão podem pular ou duplicar uma execução.
Manada estrondosa (thundering herd). O cron dispara os jobs 0 * * * * exatamente no :00 em toda a sua frota. Adicione RandomizedDelaySec= para distribuir a carga:
[Timer]
OnCalendar=hourly
RandomizedDelaySec=300
Persistent=trueIsso introduz um jitter de até cinco minutos em cada execução — algo inestimável quando cem hosts batem na mesma API.
Ambiente e diretório de trabalho. O ambiente esparso do cron derruba as pessoas; supor um diretório de trabalho também. Seja explícito no service:
[Service]
Type=oneshot
WorkingDirectory=/opt/app
Environment=PATH=/usr/local/bin:/usr/bin:/bin
EnvironmentFile=-/etc/app/env
ExecStart=/opt/app/run.shO - no início de EnvironmentFile significa “não falhe se o arquivo estiver ausente”, espelhando o comportamento tolerante do cron.
Jobs por usuário. Um crontab de usuário mapeia para uma unit de usuário. Coloque os arquivos em ~/.config/systemd/user/, habilite com systemctl --user enable --now myjob.timer e rode loginctl enable-linger $USER para que o timer sobreviva ao logout.
Verificando a migração
Depois de habilitar, confirme que o timer está armado e inspecione o seu histórico:
systemctl list-timers --all # see next/last run for every timer
journalctl -u backup.service --since today # read the job's output
sudo systemctl start backup.service # trigger a manual run to test nowsystemctl start backup.service executa o job imediatamente, de forma independente do agendamento — a maneira mais limpa de confirmar que a metade do service funciona antes de confiar no timer.
Não traduza cada campo na mão
A parte mecânica — transformar cinco campos do cron em uma linha OnCalendar e montar o par .timer/.service — é exatamente o tipo de coisa fácil de errar sutilmente na mão, especialmente com valores de passo, intervalos e casos extremos de dia da semana. Nosso Cron to systemd Converter faz isso no navegador: cole uma linha de crontab e obtenha uma unit de timer e de service pronta para editar, com a expressão OnCalendar correta e notas de migração, sem nada enviado para lugar nenhum.
Posts relacionados
-
8 min de leitura
Comando chown no Linux: mudar dono e grupo de arquivos
O comando chown no Linux explicado: sintaxe user:group, chown -R e links simbólicos, --reference, chgrp, chmod vs chown e como corrigir permissões de volumes Docker com IDs numéricos.
-
11 min de leitura
Comando chmod no Linux: sintaxe, exemplos e erros comuns
O comando chmod no Linux explicado: modos octal e simbólico, chmod +x, chmod -R e suas armadilhas, setuid, setgid e sticky bit, umask e como resolver Permission denied.
-
6 min de leitura
Lendo expressões cron: um guia campo a campo
Um guia prático, campo a campo, para ler expressões cron — os cinco campos de tempo, intervalos, passos, listas e @macros — mais as pegadinhas mais comuns.