Agendamento cron
Cron todo dia às 9h
A expressão cron para rodar todo dia às 9h é 0 9 * * *: minuto 0 da hora 9, todos os dias da semana. Se isso é 9h no seu relógio depende do fuso do agendador.
| Expressão | 0 9 * * * |
|---|---|
| Execuções | 365 vezes em 2027 |
| OnCalendar do systemd | *-*-* 09:00:00 |
Como funciona
9 no campo de hora com 0 no campo de minuto dá 09:00. Tarefas de início de expediente são onde o fuso horário mais atrapalha, porque a ideia é justamente que as pessoas vejam o resultado ao começar o dia.
Cada plataforma trata o fuso de um jeito. O cronie lê CRON_TZ=America/Sao_Paulo na linha de cima; o Kubernetes usa spec.timeZone; o GitHub Actions aceita timezone ao lado do cron; Vercel e Cloudflare só trabalham em UTC, então você converte na mão.
Para 9h no horário de Brasília num agendador em UTC, escreva 0 12 * * *. Como o Brasil não tem horário de verão desde 2019, a conversão vale o ano todo. Se a equipe está em Portugal ou nos EUA, a conversão muda duas vezes por ano.
| Campo | Valor | Significado |
|---|---|---|
| Minuto | 0 | minuto 0 (hora cheia) |
| Hora | 9 | hora 9 (9h) |
| 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 9 * * * /usr/local/bin/job.sh >> /var/log/job.log 2>&1- 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 9 * * *'
# timezone: 'America/Sao_Paulo' # opcional; sem ele, UTC
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. - Com
timezonenum fuso que tem horário de verão, um horário que não existe no dia da mudança é adiado para o próximo horário válido (2:30 vira 3:00, segundo a documentação).
CronJob do Kubernetes
apiVersion: batch/v1
kind: CronJob
metadata:
name: scheduled-job
spec:
schedule: "0 9 * * *"
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 9 * * *" }
]
}- 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. - Cabe no plano Hobby, mas lá a precisão é por hora: o disparo pode acontecer em qualquer minuto dentro da hora agendada.
Cloudflare Workers
[triggers]
crons = ["0 9 * * *"]- 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 9 * * *', 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 day at 09:00
[Timer]
OnCalendar=*-*-* 09:00:00
Persistent=true
[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 '*-*-* 09:00:00', que mostra a forma normalizada e a próxima execução.AccuracySecpadrão é 1min, então o disparo pode variar até um minuto. Persistent=trueroda a tarefa assim que a máquina liga se o horário passou com ela desligada, algo que o cron não faz.
Spring @Scheduled e Quartz
@Scheduled(cron = "0 0 9 * * *", zone = "UTC")
public void runJob() {
// ...
}
// Quartz CronTrigger:
CronScheduleBuilder.cronSchedule("0 0 9 * * ?")- 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
Servidor em UTC, equipe em Brasília
Em UTC, 0 9 * * * roda às 06:00 no horário de Brasília. É o erro mais comum com relatórios matinais em GitHub Actions e Vercel: o fuso do agendador não é o seu.
Fim de semana incluído
0 9 * * * roda também no sábado e no domingo. Só dias úteis é 0 9 * * 1-5; veja todo dia útil às 9h.
Horário de verão em outros países
Se a tarefa atende quem vive com horário de verão, um agendamento em UTC fica uma hora adiantado ou atrasado metade do ano. Use um agendador com fuso ou rode nas duas horas UTC possíveis e saia cedo se a hora local não for 9.
Perguntas frequentes
- Qual é a expressão cron para todo dia às 9h?
- 0 9 * * *, que roda às 09:00 no fuso do agendador, todos os dias.
- Como agendar um cron às 9h no horário de Brasília?
- No cronie, coloque CRON_TZ=America/Sao_Paulo na linha de cima. No Kubernetes, defina spec.timeZone. No GitHub Actions, adicione timezone ao lado do cron. Em plataformas só UTC, use 0 12 * * *.
- Como rodar às 9h30 todo dia?
- 30 9 * * * roda às 09:30.
- Como rodar às 9h e às 17h?
- 0 9,17 * * * roda às 09:00 e às 17:00 todos os dias.
Revisado em por Arielton Oberek.