Cron no Kubernetes vs crontab Linux: armadilhas de fuso
Por que a mesma expressão cron de cinco campos dispara em horários diferentes no GitHub Actions, CronJob K8s e crontab — e como visualizar com segurança.
0 9 * * 1-5 parece inequívoco: minuto zero, hora nove, dias úteis. Até você implantar em três plataformas e os backups rodarem às 09:00 local, 09:00 UTC e 09:00 no fuso do control plane — tudo com a mesma string.
Esta comparação é para quem copia linhas cron entre servidores Linux, Kubernetes e CI sem reler regras de fuso.
Mesma gramática, relógios diferentes
O cron Unix clássico usa cinco campos (minuto, hora, dia do mês, mês, dia da semana). A gramática é familiar; o relógio não é compartilhado:
| Plataforma | Comportamento típico de fuso |
|---|---|
crontab de usuário Linux | Fuso local do servidor (timedatectl) |
CronJob Kubernetes | Default do controller salvo timeZone (1.25+) |
schedule GitHub Actions | Sempre UTC |
| Quartz / algumas clouds | Muitas vezes seis campos (segundos primeiro) |
Uma linha copiada do /etc/crontab de uma VM em Shanghai para um workflow GitHub desloca oito horas se você não converter.
Crontab Linux: conheça o servidor
crontab -e na VM avalia expressões no fuso local da máquina. Mudanças de horário de verão podem pular ou repetir uma hora em agendamentos de relógio de parede como 30 2 * * *.
Runbooks devem registrar:
- Expressão
- Saída de
timedatectl - Equivalente UTC esperado para correlacionar logs
Ao cruzar com timestamps no CloudWatch ou Loki, converta com o Conversor de timestamp em vez de conta mental no incidente.
Kubernetes: defina timeZone explicitamente
Desde Kubernetes 1.25, CronJob suporta:
spec:
timeZone: "Asia/Shanghai"
schedule: "0 9 * * 1-5"
Sem timeZone, o comportamento depende da configuração do controller — não assuma que bate com seu laptop. Trate timezone ausente no YAML como bloqueio em review.
Upgrades de cluster e control planes multi-região são motivo para zonas IANA explícitas em vez de “sempre usamos local”.
GitHub Actions: só UTC
on:
schedule:
- cron: "0 9 * * 1-5"
Isso é 09:00 UTC de segunda a sexta. Para 09:00 em Shanghai em dia útil, você precisa de 0 1 * * 1-5 em UTC no offset padrão — ou documente a tabela de offset no README do workflow.
Muitos tickets “por que o CI rodou de noite?” começam aqui.
Visualize antes do merge
Em qualquer plataforma:
- Faça parse na Expressão Cron
- Leia a descrição humana (atenção à semântica OR entre dia do mês e dia da semana)
- Visualize próximas execuções em UTC e no fuso alvo
- Acrescente timezone + plataforma na linha do runbook
Falhas comuns de copiar e colar
| Erro | Resultado |
|---|---|
| Cron Actions pensado como 9h local | Job no horário errado |
K8s sem timeZone | Deriva após mudança de cluster |
| Quartz de seis campos colado como cinco | Agenda inválida ou errada |
| DOM + DOW ambos setados esperando AND | Execuções extras inesperadas |
Aprendizado relacionado
Sintaxe de campos, tabelas de plataforma e checklist de depuração no curso de expressões Cron. Para logs entre fusos, combine com Timestamps em logs entre fusos.
Conclusão
Strings cron são agnósticas de fuso; agendadores não são. Documente plataforma + zona ao lado de cada linha, visualize em UTC e local, e defina timeZone no Kubernetes de propósito — não por acidente.