Expressões cron explicadas: campos, passos e fusos horários
Cron é o agendador que roda comandos em horários fixos nos sistemas do tipo Unix, e seu formato de cinco campos se espalhou muito além dele: GitHub Actions, Kubernetes e muitos painéis de hospedagem usam a mesma notação. Uma linha como 30 2 * * 1 se lê “às 02:30 toda segunda-feira”. Quase tudo no formato é simples; algumas regras não são, e são elas que explicam tarefas que rodam na hora errada, duas vezes ou nunca. Este guia segue o padrão POSIX e o cronie, um cron muito usado no Linux, e indica onde outros agendadores são diferentes.
Os cinco campos
Uma linha de crontab tem cinco campos de tempo separados por espaços, seguidos do comando. A tarefa roda em cada minuto que atende a todos eles, com uma exceção para os dois campos de dia, explicada mais abaixo. O padrão numera os dias da semana de 0 a 6, com 0 para domingo; o cronie e a maioria dos cron do Linux aceitam também 7 para domingo, e nomes em inglês de três letras, como mon ou jan, em maiúsculas ou minúsculas.
| Campo | Valores | Exemplo | Significa |
|---|---|---|---|
| Minuto | 0–59 | 30 | no minuto 30 |
| Hora | 0–23 | 2 | das 02:00 às 02:59 |
| Dia do mês | 1–31 | 1,15 | nos dias 1 e 15 |
| Mês | 1–12 ou jan–dec | * | todo mês |
| Dia da semana | 0–7 ou sun–sat (0 e 7 são domingo) | 1-5 | de segunda a sexta |
Um asterisco quer dizer todos os valores do campo. As horas seguem o relógio de 24 horas: 9 é 09:00 e 21 é 21:00.
Listas, intervalos e passos
Um campo pode ter uma lista (1,15), um intervalo (9-17) ou um passo. O passo divide um intervalo: 0-30/10 no campo de minutos são os minutos 0, 10, 20 e 30, e */15 percorre todos os valores a partir do primeiro, ou seja, os minutos 0, 15, 30 e 45. Os passos contam a partir do início do intervalo e recomeçam a cada nova hora, dia ou mês; não continuam do anterior.
*/15 * * * *A cada 15 minutos: nos minutos 0, 15, 30 e 45.0 9-17 * * 1-5Na hora cheia das 09:00 às 17:00, de segunda a sexta.0 0 */7 * *À meia-noite nos dias 1, 8, 15, 22 e 29: a contagem recomeça todo mês.*/25 * * * *Nos minutos 0, 25 e 50, então entre :50 e o :00 seguinte são 10 minutos, não 25.
No campo de minutos, só os passos que dividem 60 exatamente (1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30) dão intervalos iguais. Para rodar a cada 90 minutos são necessárias duas linhas: 0 0-21/3 * * * roda às 00:00, 03:00 … 21:00, e 30 1-22/3 * * * às 01:30, 04:30 … 22:30.
O cronie também aceita atalhos: @hourly, @daily (0 0 * * *), @weekly (0 0 * * 0), @monthly (0 0 1 * *), @yearly ou @annually (0 0 1 1 *) e @reboot, que roda uma vez na inicialização. Eles não fazem parte do padrão: o GitHub Actions, por exemplo, não os aceita.
Dia do mês ou dia da semana
Quando os campos de dia do mês e de dia da semana têm algo diferente de um asterisco, o padrão diz que um dia vale se qualquer um dos dois bater, não os dois. Por isso 0 9 13 * 5 roda às 09:00 todo dia 13 e toda sexta-feira, e não só na sexta-feira 13. Em novembro de 2026 são os dias 6, 13 (uma sexta, executado uma vez), 20 e 27.
Para combinar os dois, agende um campo e deixe o comando conferir o outro. Num crontab, um % no comando vira quebra de linha, então ele precisa ser escrito \%:
0 9 13 * * [ "$(date +\%u)" = 5 ] && /usr/local/bin/jobSó na sexta-feira 13: roda todo dia 13, e a tarefa começa quando o dia da semana é 5 (sexta).0 18 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && /usr/local/bin/reportO último dia do mês, com o GNU date (Linux): roda do dia 28 ao 31, e a tarefa começa quando amanhã é dia 1.
Fusos horários e horário de verão
O cron lê o agendamento no fuso horário da máquina ou do serviço que o executa, que em servidores e plataformas hospedadas costuma ser UTC. 0 9 * * * num servidor em UTC roda sempre às 06:00 em São Paulo, já que o Brasil não tem horário de verão; em Lisboa e Londres, às 09:00 ou 10:00, e em Paris, Berlim, Madri ou Roma, às 10:00 ou 11:00, conforme a época do ano. O cronie deixa um crontab definir o próprio fuso com a variável CRON_TZ; o GitHub Actions aceita uma chave timezone ao lado de cron; o Kubernetes tem o campo .spec.timeZone, estável desde a versão 1.27, e recusa CRON_TZ ou TZ escritos dentro do agendamento.
Onde há horário de verão, algumas horas locais somem ou se repetem. Em 29 de março de 2026 os relógios de Paris, Berlim, Madri e Roma pulam das 02:00 para as 03:00, então 30 2 * * * indica uma hora que não existe; em 25 de outubro de 2026 a hora das 02:00 às 03:00 acontece duas vezes. Em Lisboa e Londres é a hora da 01:00 às 02:00.
| Agendador | Tarefa na hora pulada | Tarefa na hora repetida |
|---|---|---|
| cronie (mudanças de menos de 3 horas, tarefas em hora fixa ou menos frequentes que de hora em hora) | roda logo depois da mudança | roda uma vez |
| GitHub Actions (com timezone) | passa para a próxima hora válida: 02:30 vira 03:00 | não informado |
| AWS EventBridge Scheduler (com fuso horário) | é pulada | roda uma vez |
| Agendamentos em UTC | sem efeito | sem efeito |
O cronie agenda normalmente as tarefas que rodam mais de uma vez por hora, e o manual do crontab avisa de modo geral que horas que faltam nunca batem e horas repetidas podem disparar duas vezes, então o comportamento muda de um cron para outro. O mais seguro é manter o agendamento em UTC ou evitar as horas entre 01:00 e 03:00 locais para tarefas que precisam rodar exatamente uma vez por dia.
O ambiente em que a tarefa roda
Uma tarefa do cron não recebe o ambiente do seu terminal. O padrão executa o comando com sh e um conjunto mínimo de variáveis (HOME, LOGNAME, PATH e SHELL), então um comando que funciona quando você digita pode falhar no cron porque um programa não está no PATH ou falta uma variável. Use caminhos completos ou defina PATH no início do crontab.
Tudo o que o comando imprime é enviado por e-mail ao dono do crontab; o cronie envia para o endereço em MAILTO e não envia nada se MAILTO estiver vazia. Num servidor sem e-mail configurado essa saída se perde com facilidade, então é melhor mandá-la para um arquivo de log.
0 3 * * * /usr/local/bin/backup >> /var/log/backup.log 2>&1Acrescenta a saída normal e os erros a um log.5 0 * * * tar czf /srv/backup-$(date +\%F).tgz /srv/dataGrava um arquivo compactado com a data no nome: os sinais de % estão escapados.*/5 * * * * flock -n /tmp/sync.lock /usr/local/bin/syncPula uma execução enquanto a anterior ainda segura a trava.
O cron inicia cada execução na hora marcada, tenha a anterior terminado ou não, então uma tarefa que às vezes demora mais que o intervalo pode acabar rodando duas vezes ao mesmo tempo. O flock, do util-linux, mantém um arquivo de trava enquanto o comando roda; com -n ele desiste na hora, com código de saída 1, se a trava estiver ocupada. O Kubernetes tem a mesma escolha embutida: concurrencyPolicy é Allow por padrão e pode ser Forbid (pular a nova execução) ou Replace (parar a anterior).
O mesmo agendamento em outras plataformas
O formato de cinco campos não é universal. Alguns agendadores acrescentam segundos ou um ano, outros exigem um ponto de interrogação num dos dois campos de dia porque não aceitam a regra de um ou outro, e outros numeram os dias da semana a partir de 1. “Dias úteis às 09:00” em cada um:
| Agendador | Expressão | Observações |
|---|---|---|
| Cron do Linux (cronie) | 0 9 * * 1-5 | Fuso da máquina, ou CRON_TZ. |
| GitHub Actions | 0 9 * * 1-5 | UTC, a menos que timezone seja definido; no máximo uma vez a cada 5 minutos; sem atalhos com @. |
| Kubernetes CronJob | 0 9 * * 1-5 | Fuso do controlador, ou .spec.timeZone. |
| AWS EventBridge | cron(0 9 ? * MON-FRI *) | Seis campos com o ano; ? num campo de dia; os dias 1–7 começam no domingo; as regras agendadas usam UTC. |
| Quartz (Java) | 0 0 9 ? * MON-FRI | Segundos primeiro, ano opcional; ? num campo de dia; os dias 1–7 começam no domingo. |
| Timer do systemd | OnCalendar=Mon..Fri *-*-* 09:00:00 | O dia da semana e a data precisam bater os dois; *-*~01 é o último dia do mês. |
Ao copiar um agendamento entre sistemas, confira o número de campos e como os dias da semana são numerados: 1 é segunda-feira no cron, mas domingo no Quartz e no EventBridge.
Por que uma tarefa não rodou
- A máquina estava desligada ou em repouso no horário: o cron não recupera execuções perdidas. Um timer do systemd com Persistent=true recupera, na inicialização seguinte.
- O agendamento está em outro fuso horário, normalmente UTC.
- O comando depende do PATH, de uma variável ou da pasta atual do seu terminal.
- Um % sem escape cortou o comando naquele ponto.
- Dia do mês e dia da semana estavam os dois definidos, e a regra de um ou outro fez a tarefa rodar em outros dias, ou ela esperava um dia que nunca chegou.
- Numa plataforma hospedada, uma execução atrasou ou foi descartada: o GitHub avisa que execuções agendadas podem atrasar nos horários de pico, como o início de cada hora, e desativa os agendamentos de repositórios públicos depois de 60 dias sem atividade.
Antes de confiar num agendamento, releia campo por campo e, no Linux, confira as próximas execuções: systemd-analyze calendar mostra essas datas para uma expressão de timer.
Perguntas frequentes
Domingo é 0 ou 7?
Os dois, no cronie e na maioria dos cron do Linux. O padrão POSIX só define de 0 a 6, com 0 para domingo, então 0 é a escolha portável.
O cron consegue rodar uma tarefa a cada 30 segundos?
Não: a menor unidade é o minuto. Agendadores com campo de segundos, como o Quartz, conseguem; no Linux, um timer do systemd também.
Como rodo uma tarefa a cada 90 minutos?
Com duas linhas: 0 0-21/3 * * * e 30 1-22/3 * * *. Juntas, rodam a cada hora e meia, das 00:00 às 22:30.
O que significa 0 0 * * 0?
Meia-noite no início de todo domingo. É o mesmo que @weekly no cronie.
Por que minha tarefa roda em dias que eu não escolhi?
Provavelmente porque os dois campos de dia estão definidos. O cron padrão roda a tarefa quando bate o dia do mês ou o dia da semana.
Referências
- The Open Group Base Specifications (POSIX.1-2024) — crontab
- cronie — crontab(5): tables for driving cron
- cronie — cron(8): daylight saving time and other time changes
- util-linux — flock(1)
- GitHub Docs — Events that trigger workflows: schedule
- Kubernetes documentation — CronJob
- Amazon EventBridge — Cron expressions for scheduled rules
- Amazon EventBridge Scheduler — Schedule types (time zones and daylight saving)
- Quartz Scheduler — CronTrigger tutorial
- systemd — systemd.timer(5) and systemd.time(7): calendar events
Guias relacionados
- Endereços IP explicados: IPv4, IPv6 e as faixas reservadas
- Sub-redes e CIDR explicados: prefixos, máscaras e hosts utilizáveis
- Registros DNS explicados: A, AAAA, CNAME, MX, TXT e NS
- Um site está fora do ar ou é só com você? Como verificar, passo a passo
- Códigos de status HTTP e erros da Cloudflare: o que significam 403, 404, 429, 500, 503 e 521
- Timestamps Unix e fusos horários explicados
- Contagem de datas: dias entre datas, anos bissextos e semanas ISO
- Fusos horários e horário de verão: reuniões entre cidades
- Formatos de imagem explicados: JPEG, PNG, WebP, AVIF e HEIC
- O tamanho de uma imagem explicado: pixels, DPI, peso e qualidade
- Fotos de celular explicadas: HEIC, metadados EXIF e sua privacidade
- Força de senhas explicada: comprimento, entropia e frases-senha
- Funções de hash explicadas: SHA-256, checksums e hash de senhas
- JSON Web Tokens explicados: claims, assinaturas e validação segura
- Chaves de API explicadas: tamanho, formato, armazenamento e rotação
- UUIDs explicados: v4, v7 e como escolher a versão certa
- QR codes explicados: capacidade, correção de erros e tamanho de impressão
- Porcentagens e IVA explicados: somar, tirar e acumular
- Erros comuns em JSON e como corrigi-los
- Arquivos CSV explicados: separadores, aspas, codificação e Excel
- Codificações de texto explicadas: UTF-8, Base64 e codificação de URL
- Expressões regulares: classes, quantificadores e grupos
- MB ou MiB, Mbps ou MB/s: tamanho de arquivos e velocidade
- Parâmetros UTM explicados: marcar links para a análise de tráfego
- Ponto de equilíbrio, margem e ROAS: os números de um pequeno negócio
- Preço de freelancer explicado: valor da hora, diária e horas faturáveis
- Edição de PDFs no navegador: o que se mantém ao juntar, dividir e converter