Skip to content

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.

A cada hora
Expressão0 * * * *
Macro@hourly
Execuções24 vezes por dia
OnCalendar do systemdhourly

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.

Explicação campo a campo
CampoValorSignificado
Minuto0minuto 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

Relógio do agendador

Como no GitHub Actions sem timezone, na Vercel, na Cloudflare e no Kubernetes com timeZone "Etc/UTC".

Próximas execuções, calculadas no seu navegador a partir de agora
ExecuçãoSeu horárioUTC
1Calculando… 
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)

crontab -e
# 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_Paulo numa linha acima muda o fuso das linhas seguintes.

GitHub Actions

.github/workflows/scheduled.yml
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 0 por outro minuto, como 17, para atrasar menos.

CronJob do Kubernetes

cronjob.yaml
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: Forbid pula uma execução se a anterior ainda estiver rodando.

Cron Jobs da Vercel

vercel.json
{
  "crons": [
    { "path": "/api/cron", "schedule": "0 * * * *" }
  ]
}
  • A Vercel sempre usa UTC, não aceita nomes como MON ou JAN e 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

wrangler.toml
[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)

scheduler.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. noOverlap pula um disparo enquanto o anterior não terminou.

Timer do systemd

job.timer + job.service
# /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. AccuracySec padrão é 1min, então o disparo pode variar até um minuto.

Spring @Scheduled e Quartz

Java
@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.