Cheat Sheet de Crontab: 50+ expressões cron, sintaxe e guia de agendadores modernos
Uma expressão cron tem cinco campos (minuto, hora, dia do mês, mês, dia da semana) seguidos por um comando. Essa gramática controla o agendamento no Unix desde 1979 e hoje também aparece em CronJobs do Kubernetes, GitHub Actions, AWS EventBridge e gatilhos cron da Vercel.
Esta página é para quem precisa de uma expressão pronta agora: uma tarefa Linux, um CronJob no Kubernetes, um gatilho do GitHub Actions ou aquele job de cinco minutos que misteriosamente só dispara de hora em hora. Role até a Tabela de Referência Rápida para copiar expressões, vá para Sintaxe Decodificada para ver as regras de cada campo, ou abra o Gerador Crontab — uma alternativa privacy-first ao crontab guru que roda no seu navegador — para validar expressões em tempo real.
Tabela de referência rápida de expressões cron
Trinta expressões cobrindo cerca de 90% das necessidades reais de agendamento. Todas são cron POSIX de cinco campos válido. Cole no crontab -e, em um schedule: do Kubernetes ou em um cron: do GitHub Actions.
| Agendamento | Expressão cron | Em português claro |
|---|---|---|
| A cada minuto | * * * * * | todo minuto, o dia inteiro, todos os dias |
| A cada 5 minutos | */5 * * * * | minuto 0, 5, 10, …, 55 |
| A cada 15 minutos | */15 * * * * | minuto 0, 15, 30, 45 |
| A cada 30 minutos | */30 * * * * | minuto 0 e 30 |
| A cada hora | 0 * * * * | no início de cada hora |
| A cada 2 horas | 0 */2 * * * | hora 0, 2, 4, …, 22 |
| A cada 6 horas | 0 */6 * * * | hora 0, 6, 12, 18 |
| Duas vezes ao dia (9h + 21h) | 0 9,21 * * * | minuto 0 das horas 9 e 21 |
| Todo dia útil às 9h | 0 9 * * 1-5 | seg-sex 09:00 |
| Todo fim de semana às 9h | 0 9 * * 0,6 | sáb e dom 09:00 |
| Diariamente à meia-noite | 0 0 * * * | todo dia 00:00 |
| Diariamente às 2:30 | 30 2 * * * | janela de batch fora do pico |
| Toda segunda às 9h | 0 9 * * 1 | segundas 09:00 |
| Toda sexta às 17h | 0 17 * * 5 | sextas 17:00 |
| Todo domingo à meia-noite | 0 0 * * 0 | equivalente a @weekly |
| Primeiro do mês à meia-noite | 0 0 1 * * | dia 1 00:00, equivalente a @monthly |
| Dia 15 de cada mês ao meio-dia | 0 12 15 * * | janela de folha de pagamento no meio do mês |
| Verificação do último dia (wrapper) | 0 0 28-31 * * + script | precisa de checagem de data |
| Trimestral (1º de jan/abr/jul/out) | 0 0 1 JAN,APR,JUL,OCT * | primeiro dia de cada trimestre |
| Anualmente (1º de janeiro) | 0 0 1 1 * ou @yearly | meia-noite do ano novo |
| A cada 5 min em dia útil das 9 às 17 | */5 9-17 * * 1-5 | polling em horário comercial |
| A cada 30 min nos fins de semana | */30 * * * 0,6 | monitoramento sáb/dom |
| Duas vezes por hora, aos 15 e 45 | 15,45 * * * * | desviado da debandada do :00 |
| Primeira segunda-feira (wrapper) | 0 9 1-7 * 1 + verificação AND | precisa de wrapper (veja abaixo) |
| Macros | @hourly @daily @weekly @monthly @yearly | não padrão, mas amplamente suportadas |
| Apenas no boot | @reboot | não padrão, só no vixie cron |
Cole qualquer uma no Gerador Crontab para ver as próximas cinco execuções. É a checagem pré-deploy mais rápida que existe.
Sintaxe cron decodificada — os 5 campos
Uma expressão cron tem cinco campos separados por espaços em branco e um comando. Cada campo controla uma fatia do agendamento. Essa é a sintaxe base em todos os agendadores deste guia.
┌──────────── minute (0 - 59)
│ ┌────────── hour (0 - 23)
│ │ ┌──────── day-of-month (1 - 31)
│ │ │ ┌────── month (1 - 12 or JAN-DEC)
│ │ │ │ ┌──── day-of-week (0 - 6 or SUN-SAT; 0 and 7 both mean Sunday)
│ │ │ │ │
* * * * * command-to-run
Mnemônico: “Meu Hábito Dispara Manhã Sábado” — Minuto, Hora, Dia do mês, Mês, Semana (dia). Da esquerda para a direita, da menor unidade para a maior.
Valores permitidos campo a campo
| Campo | Faixa | Apelidos | Observações |
|---|---|---|---|
| Minuto | 0-59 | nenhum | 0 significa “na hora cheia” |
| Hora | 0-23 | nenhum | relógio de 24 horas; 0 é meia-noite, 12 é meio-dia |
| Dia do mês | 1-31 | nenhum | dias inválidos para um mês simplesmente nunca disparam (31 de fevereiro) |
| Mês | 1-12 | JAN, FEB, MAR, …, DEC | não diferencia maiúsculas/minúsculas |
| Dia da semana | 0-7 | SUN, MON, TUE, …, SAT | tanto 0 quanto 7 significam domingo |
Operadores em detalhe
Cinco operadores cobrem toda expressão cron padrão:
| Operador | Significado | Exemplo | Expande para |
|---|---|---|---|
* | qualquer valor | * * * * * | a cada minuto |
, | lista | 0 9,12,17 * * * | 09:00, 12:00, 17:00 |
- | faixa inclusiva | 0 9-17 * * * | toda hora das 09:00 às 17:00 |
/ | passo | */15 * * * * | minuto 0, 15, 30, 45 |
| misto | combinado | 0 9-12,14-17 * * * | manhã + tarde, pula o almoço |
Cuidado com o operador de passo. */N se ancora no menor valor do campo, não no horário atual. */15 significa “minuto 0, 15, 30, 45 de cada hora”, não “a cada 15 minutos a partir de agora”. Salvou às 12:03? A próxima execução é às 12:15. Com base não curinga, 5/15 lê-se “comece em 5, depois de 15 em 15”: minuto 5, 20, 35, 50.
Meses e dias da semana nomeados
Escreva meses e dias da semana como nomes, sem diferenciar caixa:
0 0 1 JAN,APR,JUL,OCT * # primeiro dia de cada trimestre
0 9 * * MON-FRI # dias úteis às 9h
0 17 * * FRI # sexta às 17h
Nomes ficam mais legíveis em code review; as formas numéricas são um pouco mais portáveis. Escolha um estilo por projeto.
Macros não padrão: @reboot, @daily e companhia
A maioria das implementações de cron aceita seis macros de atalho:
| Macro | Expande para | Significado |
|---|---|---|
@yearly / @annually | 0 0 1 1 * | uma vez por ano, 1º de janeiro à meia-noite |
@monthly | 0 0 1 * * | primeiro dia de cada mês à meia-noite |
@weekly | 0 0 * * 0 | todo domingo à meia-noite |
@daily / @midnight | 0 0 * * * | todo dia à meia-noite |
@hourly | 0 * * * * | no início de cada hora |
@reboot | (especial) | uma vez quando o daemon do cron inicia |
Essas macros não são padrão: vixie cron e cronie as suportam, mas CronJob do Kubernetes, GitHub Actions e AWS EventBridge não. Para expressões portáveis, escreva a forma de cinco campos. @reboot raramente funciona em contêineres, onde o daemon do cron pode não ser o processo init.
50+ expressões cron para copiar e colar (agrupadas por caso de uso)
A Tabela de Referência Rápida cobre os casos comuns. Esta seção traz seis grupos com mais exemplos de jobs cron.
A cada N minutos
* * * * * # a cada minuto
*/2 * * * * # a cada 2 minutos
*/5 * * * * # a cada 5 minutos — o caso "expressão cron a cada 5 minutos"
*/10 * * * * # a cada 10 minutos
*/15 * * * * # a cada 15 minutos
*/30 * * * * # a cada 30 minutos
0,30 * * * * # minutos explícitos 0 e 30 (igual a */30)
*/45 * * * * # ATENÇÃO: dispara só aos 0 e 45, depois reseta
*/45 é uma pegadinha comum: o minuto vai de 0-59, então cai aos 0 e 45 e reinicia na hora seguinte. Para uma cadência real de 45 minutos, é preciso um worker externo.
Variantes horárias
0 * * * * # toda hora aos :00
30 * * * * # toda hora aos :30
0 */2 * * * # a cada 2 horas, em hora par
0 */6 * * * # a cada 6 horas
0 */12 * * * # duas vezes ao dia, às 00:00 e 12:00
15 */2 * * * # a cada 2 horas, deslocado em 15 min (evita o pico de :00)
Diário em horários específicos
0 0 * * * # meia-noite (= @daily / @midnight)
30 2 * * * # 02:30 — janela de batch fora do pico
0 9 * * * # 09:00
45 23 * * * # 23:45 — fechamentos de fim de dia
0 9,12,17 * * * # três vezes ao dia
0 9-17 * * * # toda hora das 09:00 até as 17:00
Agendas semanais
0 9 * * 1-5 # dias úteis às 9h
0 9 * * 0,6 # fins de semana às 9h
0 18 * * 5 # sextas às 18h
0 0 * * 0 # domingo à meia-noite (= @weekly)
0 9 * * MON,WED,FRI # seg/qua/sex às 9h
*/30 9-17 * * 1-5 # a cada 30 min, horário comercial, dias úteis
Mensal e trimestral
0 0 1 * * # dia 1 do mês à meia-noite (= @monthly)
0 0 15 * * # dia 15 — janela de folha de pagamento
0 0 1,15 * * # dias 1 e 15 — quinzenal
0 0 1 */3 * # trimestral: dia 1 de jan, abr, jul, out
0 0 1 JAN,APR,JUL,OCT * # idem, meses nomeados
0 0 28-31 * * # últimos dias — combine com um wrapper de checagem de data
O último dia do mês não tem expressão POSIX nativa. Rode um wrapper que verifica date -d tomorrow +%d = 01, ou use um agendador com suporte nativo (Quartz tem L; Kubernetes não).
Anual e atalhos de macro
0 0 1 1 * # 1º de janeiro à meia-noite (= @yearly / @annually)
0 0 25 12 * # Natal à meia-noite
@yearly # = 0 0 1 1 *
@monthly # = 0 0 1 * *
@weekly # = 0 0 * * 0
@daily # = 0 0 * * *
@hourly # = 0 * * * *
@reboot # especial: uma vez no boot do daemon (só vixie cron)
Todas essas expressões podem ser coladas no Gerador Crontab para visualizar as próximas cinco execuções. É o teste de fumaça mais barato antes de subir para produção.
Cron vs systemd timers vs agendadores cloud — matriz de decisão
Cron é o padrão; nem sempre a melhor escolha. Os sete agendadores mais comuns lado a lado, útil para decisões cron vs systemd timer, CronJob do Kubernetes vs Vercel cron job, ou para migrar do crontab para cloud gerenciado.
| Recurso | vixie cron | systemd timer | K8s CronJob | GHA schedule | AWS EventBridge | Vercel Cron | Cloudflare Workers |
|---|---|---|---|---|---|---|---|
| Sintaxe de campo | POSIX 5 campos | spec OnCalendar | POSIX 5 campos + timeZone | POSIX 5 campos | Quartz 6 campos com ? | POSIX 5 campos | POSIX 5 campos |
| Intervalo mínimo | 1 minuto | 1 segundo | 1 minuto | best-effort, ≥15 min recomendado | 1 minuto | 1 minuto (plano Pro) | 1 minuto |
| Fuso horário explícito | CRON_TZ= | Persistent=true | spec.timeZone (1.27+) | só UTC | ScheduleExpressionTimezone | só UTC | só UTC |
| Recuperação de execução perdida | não (use anacron) | sim (Persistent=true) | sim (startingDeadlineSeconds) | não | sim | não | não |
| Retry / backoff | não | parcial | sim (backoffLimit) | retry em falha | sim | não | sim |
| Controle de concorrência | não (use flock) | parcial | sim (concurrencyPolicy) | não | não | não | não |
Suporte a @reboot | sim | sim (via OnBootSec=) | não | não | não | não | não |
systemd timers — quando preferir em vez do cron
Em Linux com systemd, timers são uma alternativa séria: sintaxe de calendário legível, integração com o journal, recuperação de execuções perdidas. Um timer e o service correspondente:
# daily-report.timer
[Unit]
Description=Run daily report at 9 AM
[Timer]
OnCalendar=*-*-* 09:00:00
Persistent=true
Unit=daily-report.service
[Install]
WantedBy=timers.target
# daily-report.service
[Unit]
Description=Daily report job
[Service]
Type=oneshot
ExecStart=/usr/local/bin/daily-report.sh
User=reporter
Habilite com systemctl enable --now daily-report.timer. O ganho prático é o Persistent=true: se a máquina estava desligada às 9h, o timer dispara assim que ela inicia. O vixie cron não tem equivalente sem anacron. Para endurecimento de serviços, veja nosso guia de boas práticas de segurança.
CronJob do Kubernetes
O Kubernetes embrulha o agendamento POSIX com primitivas para concorrência, histórico e fuso horário explícito:
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-report
spec:
schedule: "0 2 * * *"
timeZone: "America/New_York" # Kubernetes 1.27+
concurrencyPolicy: Forbid # never run two at once
startingDeadlineSeconds: 300 # skip if delayed >5 min
jobTemplate:
spec:
backoffLimit: 2
template:
spec:
restartPolicy: OnFailure
containers:
- name: reporter
image: reporter:1.4.0
command: ["/usr/local/bin/report.sh"]
concurrencyPolicy: Forbid é o equivalente ao seu flock. Sem ele, um job demorado se empilha sobre o sucessor. Veja a seção de referência de campos para todas as opções.
Pegadinhas do schedule do GitHub Actions
GitHub Actions aceita cron POSIX padrão de cinco campos:
on:
schedule:
- cron: '0 9 * * 1-5' # weekdays at 9 AM UTC
É best-effort: sob alta carga nos runners do GitHub, jobs podem disparar com minutos de atraso ou ser pulados. Evite intervalos menores que quinze minutos. Não dá para configurar fuso, é sempre UTC.
AWS EventBridge — Quartz de seis campos
AWS EventBridge usa um cron sabor Quartz com seis campos e um ? obrigatório em um dos slots de dia:
cron(0 9 * * ? *)
Ordem dos campos: Minutes Hours Day-of-month Month Day-of-week Year. Um dos campos de dia precisa ser ? quando o outro está restrito (forma do Quartz de resolver a ambiguidade do OR do POSIX). Uma cópia direta do crontab Linux falha na validação.
Vercel Cron, Cloudflare Workers, Render Cron Jobs
As plataformas serverless mais recentes padronizam em POSIX de cinco campos. Um job cron da Vercel vive no vercel.json como { "crons": [{ "path": "/api/cron/nightly", "schedule": "0 2 * * *" }] }. Os Cron Triggers do Cloudflare Workers usam wrangler.toml:
[triggers]
crons = ["*/15 * * * *", "0 9 * * 1-5"]
O Render usa render.yaml. Os três rodam em UTC sem override de fuso por agendamento. Projete em UTC desde o início.
7 armadilhas de debug em cron (e como pegá-las)
A maior parte dos relatos de “meu job cron não está rodando” tem uma de sete causas raiz. Percorra esta lista antes de culpar o agendador.
Armadilha 1: o PATH é mínimo
O cron inicia jobs com um $PATH mínimo, em geral /usr/bin:/bin. Seu shell interativo tem /usr/local/bin, ~/.cargo/bin e uma dúzia de entradas do .bashrc. Nada disso existe no cron. Essa é a causa mais comum quando você precisa depurar PATH em cron.
Sintoma: node: command not found. Solução: defina o PATH no topo do crontab ou use caminhos absolutos.
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin
*/15 * * * * /usr/local/bin/poll-api.sh
0 9 * * * /home/deploy/.cargo/bin/my-rust-cli
Armadilha 2: stdout e stderr são silenciosamente perdidos
Por padrão, a saída do cron vai para um spool de e-mail que ninguém lê. O job falha em silêncio. Redirecione os dois streams:
*/15 * * * * /usr/local/bin/job.sh >> /var/log/job.log 2>&1
Para saída JSON, pipe via jq; para extrair linhas de log, veja nosso cheat sheet de regex. Para timers do systemd, journalctl -u your-timer.service captura a saída.
Armadilha 3: deriva de fuso horário entre dev e prod
Você escreveu 0 9 * * * no seu laptop em São Paulo, esperando 9h no horário de Brasília. O servidor roda em UTC. O cron dispara às 9h UTC, ou seja, 6h em Brasília, antes de qualquer um perceber. Solução: coloque os servidores em UTC e escreva os agendamentos em UTC, ou fixe o fuso horário explicitamente.
CRON_TZ=America/Sao_Paulo
0 9 * * * /usr/local/bin/morning-report.sh
CRON_TZ funciona no vixie cron 3.0+; Kubernetes 1.27+ tem spec.timeZone; AWS EventBridge tem ScheduleExpressionTimezone; GitHub Actions é sempre UTC. Para UTC, horário de verão e aritmética de epoch, veja nosso guia de Unix timestamp.
Armadilha 4: % não escapado nos comandos
O cron trata o % não escapado como nova linha: o restante da linha é enviado como stdin para o comando. Então date +"%Y-%m-%d" quebra. Escape todo % como \%, ou jogue a lógica para dentro de um script:
0 0 * * * echo "Run at $(date +"\%Y-\%m-\%d")" >> /tmp/log
Armadilha 5: execuções sobrepostas
Um job */5 * * * * que de vez em quando leva sete minutos vai começar a próxima instância antes da anterior terminar. Duas cópias brigam pela mesma linha, pelo mesmo lock file e pela mesma cota de API. Serialize com flock:
*/5 * * * * flock -n /tmp/job.lock /usr/local/bin/job.sh
-n sai imediatamente se o lock já está em uso. No Kubernetes, defina concurrencyPolicy: Forbid. Permissões do arquivo de lock importam — veja boas práticas de segurança.
Armadilha 6: @reboot em contêineres
@reboot roda uma vez quando o daemon do cron inicia. Numa VM, isso casa com o boot. Num contêiner, o daemon do cron geralmente não é o PID 1 e pode nem rodar. Não use @reboot em contêineres. Coloque a lógica “rodar uma vez no boot” no seu entrypoint ou em um init container.
Armadilha 7: a semântica OR de dia do mês / dia da semana do POSIX
Talvez seja a armadilha mais cara do cron. Regra do POSIX: quando tanto dia do mês quanto dia da semana estão restritos (nenhum dos dois é *), o agendamento dispara quando qualquer dos dois bate.
0 0 1 * 5 parece “meia-noite do dia 1, só nas sextas”, mas dispara no dia 1 E em toda sexta: seis a dez disparos extras por mês.
# ERRADO: parece "dia 1 do mês, só se for sexta"
0 0 1 * 5
# CERTO: escolha uma restrição
0 0 1 * * # todo dia 1 do mês
0 0 * * 5 # toda sexta
# Semântica AND exige um wrapper
0 0 1-7 * 5 [ "$(date +\%u)" = "5" ] && /script # só a primeira sexta
Cole expressões suspeitas no Gerador Crontab. A prévia da próxima execução mostra na hora a armadilha do OR.
Agendadores modernos — quando NÃO usar cron
Cron funciona bem para “rode esse comando mais ou menos nesse horário, em cadência fixa”. Não funciona para vários problemas vizinhos:
- Workflows com dependências (rode A, depois B se A teve sucesso): use Airflow, Prefect ou Dagster.
- Retry, backoff exponencial, dead-letter queues: use Temporal, AWS Step Functions ou Sidekiq.
- Intervalos abaixo de um minuto: um worker de vida longa que dorme entre as iterações.
- Precisão de segundos: daemon dedicado; agendadores gerenciados nem prometem timing exato.
- Trabalho orientado a eventos: webhooks, filas de mensagens, streams de change-data-capture.
Cron continua útil. Airflow, Step Functions e Sidekiq aceitam expressões cron como entrada dos seus workflows. A gramática de cinco campos é reaproveitável.
Referência de campos do Kubernetes CronJob
A matriz de decisão acima mostrou um CronJob mínimo. A referência completa de campos da sintaxe de cronjob do Kubernetes:
| Campo | Padrão | O que faz |
|---|---|---|
schedule | obrigatório | expressão cron POSIX de 5 campos |
timeZone | TZ do controller | fuso horário explícito (1.27+); use nomes IANA |
concurrencyPolicy | Allow | Forbid pula novas execuções enquanto a anterior estiver ativa; Replace cancela a anterior |
startingDeadlineSeconds | sem limite | pula se o atraso for maior que isso |
successfulJobsHistoryLimit | 3 | Jobs bem-sucedidos a manter |
failedJobsHistoryLimit | 1 | Jobs falhos a manter |
suspend | false | pausa sem deletar |
backoffLimit | 6 | retries de Pod antes do Job ser marcado como Failed |
activeDeadlineSeconds | sem definição | teto rígido para o runtime do Pod |
ttlSecondsAfterFinished | sem definição | deleta o Job automaticamente após esses segundos |
Duas pegadinhas comuns: esquecer do timeZone faz o agendamento seguir o fuso do host do kube-controller-manager (imprevisível em Kubernetes gerenciado); num agendamento de um minuto, o padrão successfulJobsHistoryLimit: 3 acumula três objetos Job por minuto se ttlSecondsAfterFinished não estiver definido.
Equivalentes de cron em outras plataformas
macOS launchd. A Apple recomenda launchd em vez do cron. Um job do launchd é um .plist em ~/Library/LaunchAgents/:
<plist version="1.0"><dict>
<key>Label</key><string>com.example.daily</string>
<key>ProgramArguments</key><array><string>/usr/local/bin/daily.sh</string></array>
<key>StartCalendarInterval</key>
<dict><key>Hour</key><integer>9</integer><key>Minute</key><integer>0</integer></dict>
</dict></plist>
Carregue com launchctl load ~/Library/LaunchAgents/com.example.daily.plist. Diferente do cron, o launchd recupera execuções perdidas após sleep/wake.
Windows Task Scheduler usa schtasks:
schtasks /create /tn "DailyReport" /tr "C:\scripts\report.bat" /sc DAILY /st 09:00
schtasks /create /tn "EveryFifteen" /tr "C:\scripts\poll.bat" /sc MINUTE /mo 15
No WSL, o cron nativo do Linux funciona, mas para quando a sessão encerra. Use o Task Scheduler para disparar jobs WSL sempre ativos.
Cron em contêineres Docker. A maioria das imagens slim (alpine, debian-slim, distroless) não vem com um daemon de cron. Instale cronie ou busybox-cron e rode como PID 1 com tini ou s6-overlay. Em quase todos os casos, vale mais a pena usar um CronJob do Kubernetes.
Dicas e padrões avançados
Último dia do mês
Cron não tem operador nativo de “último dia”. Rode todo dia na janela 28-31 e verifique se amanhã é dia 1:
0 23 28-31 * * [ "$(date -d tomorrow +\%d)" = "01" ] && /usr/local/bin/eom.sh
N-ésimo dia da semana do mês
“Primeira segunda” usa o mesmo padrão de wrapper: restrinja aos dias 1-7 e cheque o dia da semana:
0 9 1-7 * * [ "$(date +\%u)" = "1" ] && /usr/local/bin/first-monday.sh
Para “última sexta”, use dias 25-31 mais a checagem do dia da semana.
Offset aleatório para distribuir carga
Quando várias máquinas rodam o mesmo cron, 0 0 * * * produz uma debandada à meia-noite UTC. Espalhe com um atraso aleatório:
RANDOM_DELAY=10 # cronie / anacron, em minutos
0 0 * * * /usr/local/bin/job.sh
0 0 * * * sleep $((RANDOM \% 600)); /usr/local/bin/job.sh # portável
Monitoramento de heartbeat
Cron falha em silêncio. O padrão dead-man’s-switch: o job dá um ping num serviço de monitoramento após cada execução bem-sucedida; o serviço alerta quando um ping esperado não chega. Healthchecks.io, Cronitor e Dead Man’s Snitch oferecem planos gratuitos.
*/15 * * * * /usr/local/bin/job.sh && curl -fsS --retry 3 https://hc-ping.com/your-uuid
Para lógica de monitoramento que ramifica em códigos de resposta (200 saudável, 429 rate-limited, 503 degradado), veja nosso cheat sheet de códigos de status HTTP.
Idempotência é uma propriedade do job, não do agendador
Cron não tem retry, nem recuperação de execução perdida, nem controle de concorrência. A correção mais confiável é tornar o próprio job seguro para rodar várias vezes. Em vez de “envie o relatório de hoje às 9h”, desenhe como “envie o relatório de hoje se ainda não foi enviado”. Execuções perdidas, duplicadas e catch-ups manuais convergem para o mesmo estado.
FAQ
*/5 * * * * é realmente a cada 5 minutos?
Quase — */5 * * * * está ancorado ao minuto 0, não “a cada 5 minutos a partir de agora”. Dispara no minuto 0, 5, 10, …, 55 de cada hora. O passo */N é relativo ao menor valor do campo, não ao horário atual. Salvou às 12:03? A próxima execução é 12:05, não 12:08.
O que significa 0 0 * * * no cron?
0 0 * * * significa todo dia à meia-noite (00:00) no fuso horário local do servidor. Campos: minuto 0, hora 0, qualquer dia do mês, qualquer mês, qualquer dia da semana. Equivale às macros @daily ou @midnight. Para fixar o fuso, adicione CRON_TZ=America/Sao_Paulo no topo do crontab.
Como rodar um job cron a cada 30 segundos?
Não dá com cron POSIX padrão. A granularidade mínima é de um minuto. Três contornos: dois jobs intercalados em * * * * * com sleep 30 && em um deles; um timer systemd com OnCalendar=*:*:0/30; ou um worker de vida longa que dorme entre iterações. O último costuma ser o caminho certo.
Qual fuso horário o cron usa por padrão?
O fuso local do sistema no servidor (/etc/timezone ou a variável de ambiente TZ). Um cron das 9h em um servidor UTC dispara às 6h em Brasília. Solução: defina CRON_TZ= no topo do crontab, ou rode os servidores em UTC e desenhe os agendamentos em UTC. GitHub Actions é sempre UTC; Kubernetes 1.27+ suporta spec.timeZone.
Por que meu job cron não está rodando?
Se sua tarefa cron não está rodando, cheque nesta ordem: o daemon do cron está rodando (systemctl status cron); o $PATH está definido no crontab; o stderr está sendo capturado (>> log 2>&1); o crontab do usuário foi carregado (crontab -l); o % está escapado nos comandos; o fuso horário é o esperado. A maior parte dos relatos “não está rodando” para no segundo ou terceiro item.
A sintaxe do CronJob do Kubernetes é igual à do cron do Linux?
Sim para o campo schedule: os dois usam cron POSIX de cinco campos. O Kubernetes adiciona spec.timeZone (1.27+), concurrencyPolicy para controle de sobreposição, startingDeadlineSeconds para recuperação de execução perdida e suspend: true para pausar. O cron do Linux não tem nada disso. Recorra a flock e anacron.
Qual a diferença entre @reboot e @daily?
@daily é uma macro para 0 0 * * *, ou seja, todo dia à meia-noite em agendamento fixo. @reboot roda uma vez quando o daemon do cron inicia, sem agendamento recorrente. @reboot é suportado por vixie cron e cronie, mas não por CronJob do Kubernetes, GitHub Actions ou AWS EventBridge. Em contêineres, @reboot raramente dispara.
Qual é a diferença entre cron e crontab?
cron é o daemon em segundo plano que executa tarefas agendadas; crontab é o arquivo que lista essas tarefas (e o comando crontab para editá-lo). O daemon lê o crontab de cada usuário em ciclo e executa os comandos cuja hora de execução bate com a expressão cron. Em resumo: cron é o motor, crontab é a receita.