Agendamento cron
Cron a cada hora
Para rodar um cron a cada hora, use 0 * * * *, ou a macro @hourly. Ele dispara no minuto 0 de toda hora, 24 vezes por dia.
| Expressão | 0 * * * * |
|---|---|
| Macro | @hourly |
| Execuções | 24 vezes por dia |
| OnCalendar do systemd | hourly |
Como funciona
É o campo de minuto que torna isso horário. Um 0 fixo quer dizer “só no minuto 0”, e o * no campo de hora deixa isso valer em todas as horas. Qualquer outro minuto fixo também roda de hora em hora: 17 * * * * é toda hora aos 17 minutos.
O @hourly é definido no crontab(5) como exatamente 0 * * * *, e Kubernetes, Spring e a maioria das bibliotecas aceitam. A documentação do GitHub diz que o Actions não aceita as macros com @, e Vercel e Cloudflare documentam só os cinco campos, então escreva os campos nesses casos.
As distribuições escolhem um minuto quebrado para as próprias tarefas horárias, para fugir da multidão da hora cheia: o /etc/crontab do Debian roda o /etc/cron.hourly no minuto 17, e o /etc/cron.d/0hourly do Fedora no minuto 01. Copie a ideia se a sua tarefa usa infraestrutura compartilhada.
| Campo | Valor | Significado |
|---|---|---|
| Minuto | 0 | minuto 0 (hora cheia) |
| Hora | * | toda hora |
| Dia do mês | * | todo dia do mês |
| Mês | * | todo mês |
| Dia da semana | * | todo dia da semana |
Próximas execuções
Como no GitHub Actions sem timezone, na Vercel, na Cloudflare e no Kubernetes com timeZone "Etc/UTC".
| Execução | Seu horário | UTC |
|---|---|---|
| 1 | Calculando… | |
| 2 | ||
| 3 | ||
| 4 | ||
| 5 |
Versões prontas para colar
O mesmo agendamento em cada agendador, com o que muda em cada um.
crontab (Linux, macOS, BSD)
# m h dom mon dow command
0 * * * * /usr/local/bin/job.sh >> /var/log/job.log 2>&1
# Same, but skip a run while the previous one is still going:
0 * * * * flock -n /tmp/job.lock /usr/local/bin/job.sh- O cron usa o fuso horário do servidor. No cronie (Fedora, RHEL),
CRON_TZ=America/Sao_Paulonuma linha acima muda o fuso das linhas seguintes.
GitHub Actions
on:
schedule:
- cron: '0 * * * *'
workflow_dispatch: {}- Roda em UTC, a menos que você defina
timezone, e só no branch padrão. A documentação do GitHub avisa que execuções agendadas podem atrasar em horários de pico e que, em repositórios públicos, o agendamento é desativado após 60 dias sem atividade. - O início de cada hora é o horário de maior fila no GitHub. Se o minuto exato não importa, troque o
0por outro minuto, como17, para atrasar menos.
CronJob do Kubernetes
apiVersion: batch/v1
kind: CronJob
metadata:
name: scheduled-job
spec:
schedule: "0 * * * *"
timeZone: "Etc/UTC"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: job
image: busybox:1.36
command: ["/bin/sh", "-c", "date; echo running"]spec.timeZoneé estável desde o Kubernetes 1.27; sem ele, vale o fuso do kube-controller-manager.concurrencyPolicy: Forbidpula uma execução se a anterior ainda estiver rodando.
Cron Jobs da Vercel
{
"crons": [
{ "path": "/api/cron", "schedule": "0 * * * *" }
]
}- A Vercel sempre usa UTC, não aceita nomes como
MONouJANe não deixa definir dia do mês e dia da semana ao mesmo tempo. - Roda mais de uma vez por dia, então precisa do plano Pro ou Enterprise. No Hobby, a documentação diz que o deploy falha com expressões que rodam mais de uma vez por dia.
Cloudflare Workers
[triggers]
crons = ["0 * * * *"]- Cron Triggers rodam em UTC. Mudanças podem levar até 15 minutos para se propagar pela rede da Cloudflare.
node-cron (Node.js)
import cron from 'node-cron';
// 6 campos: o primeiro (segundos) é opcional
cron.schedule('0 0 * * * *', async () => {
await runJob();
}, { timezone: 'UTC', noOverlap: true });- O agendamento vive dentro do processo Node: se ele cair, nada roda; se houver três réplicas, a tarefa roda três vezes.
noOverlappula um disparo enquanto o anterior não terminou.
Timer do systemd
# /etc/systemd/system/job.timer
[Unit]
Description=Run job.service every hour
[Timer]
OnCalendar=hourly
[Install]
WantedBy=timers.target
# /etc/systemd/system/job.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/job.sh
# systemctl daemon-reload && systemctl enable --now job.timer- Confira com
systemd-analyze calendar 'hourly', que mostra a forma normalizada e a próxima execução.AccuracySecpadrão é 1min, então o disparo pode variar até um minuto.
Spring @Scheduled e Quartz
@Scheduled(cron = "0 0 * * * *", zone = "UTC")
public void runJob() {
// ...
}
// Quartz CronTrigger:
CronScheduleBuilder.cronSchedule("0 0 * * * ?")- No Spring, o primeiro dos 6 campos são os segundos; o resto segue o cron comum (0 ou 7 = domingo).
- No Quartz, um dos campos de dia precisa ser
?, por isso o dia da semana leva?.
Armadilhas
* * * * * ou * */1 * * * é a cada minuto, não a cada hora
Um asterisco no campo de minuto casa com os 60 minutos. * */1 * * * continua rodando 60 vezes por hora. Numa tarefa horária o minuto tem que ser um número fixo.
A hora cheia é o minuto mais disputado
A documentação do GitHub aponta o início de cada hora como horário de carga alta, em que workflows agendados atrasam ou até são descartados. Mudar para 17 * * * * ou parecido não custa nada e costuma ajudar.
Horário de verão muda a contagem
Em fusos com horário de verão, no dia em que o relógio adianta uma tarefa horária em hora local roda 23 vezes. No dia em que atrasa, o relógio mostra 01:00 duas vezes, e o cronie, cujo tratamento de horário de verão no cron(8) só vale para tarefas de horário fixo, roda nas duas. Agende em UTC se precisa de exatamente 24 execuções por dia.
Perguntas frequentes
- Qual é a expressão cron para rodar a cada hora?
- 0 * * * *, ou seja, minuto 0 de toda hora. A macro @hourly é idêntica onde é suportada.
- @hourly é o mesmo que 0 * * * *?
- Sim. O crontab(5) define @hourly como 0 * * * *, e o Kubernetes documenta a mesma equivalência.
- Como rodar um cron toda hora aos 30 minutos?
- 30 * * * * roda às 00:30, 01:30, 02:30 e assim por diante.
- Como rodar de hora em hora só entre 8h e 18h?
- 0 8-18 * * * roda na hora cheia das 08:00 às 18:00, onze vezes por dia.
Revisado em por Arielton Oberek.