Guide
Tutorial de Expressões Cron: Como Ler e Escrever Agendamentos no Crontab
Cinco campos, seis caracteres especiais e um bug histórico que decide se seu job roda uma ou duas vezes.
By Buğra SözeriPublished
O cron tem quarenta anos e ainda é o agendador padrão em quase todo sistema Unix lançado este ano. A sintaxe cabe em uma linha, e esse é o principal motivo pelo qual sobreviveu — e também por que é lida de forma errada com regularidade. Este guia percorre os cinco campos, cada caractere especial, a peculiaridade dia-do-mês vs dia-da-semana, e uma pequena biblioteca de expressões que você pode copiar diretamente para um crontab.
A anatomia dos cinco campos
Uma expressão cron POSIX tem cinco campos separados por espaço em branco, nesta ordem:
# ┌──────────── minuto (0 - 59)
# │ ┌────────── hora (0 - 23)
# │ │ ┌──────── dia do mês (1 - 31)
# │ │ │ ┌────── mês (1 - 12) ou JAN-DEC
# │ │ │ │ ┌──── dia da semana (0 - 6) ou SUN-SAT, onde 0 e 7 são domingo
# │ │ │ │ │
* * * * * comando-a-executarCada campo deve estar presente. Não há valores padrão e nem atalhos posicionais. Um campo com *significa “todo valor válido neste intervalo”, não “ignorar.” Essa distinção importa assim que você começa a combinar campos.
Algumas variações adicionam um sexto campo de segundos no início (Quartz, @Scheduled do Spring) ou um sétimo campo de ano no final. São extensões, não POSIX. Se estiver escrevendo para portabilidade, fique com os cinco.
Por que dois campos de dia?
O cron permite dizer “toda segunda-feira” e “o primeiro do mês” na mesma linha, o que seria ambíguo se ambos os campos precisassem concordar. A solução histórica é a semântica OR quando ambos os campos são restritos: o job dispara quando dia-do-mês ou dia-da-semana corresponde. Voltaremos a isso; é o bug de cron mais comum.
Caracteres especiais
Cada campo aceita seis caracteres especiais. Seu comportamento é idêntico em todos os campos, mas o intervalo válido obviamente difere por campo.
* — todo valor
Corresponde a todo valor válido no campo. * * * * * significa todo minuto de toda hora de todo dia. Esta é a única expressão que genuinamente dispara sessenta vezes por hora; qualquer coisa mais complexa geralmente dispara menos.
, — lista
A vírgula separa valores discretos. 0,15,30,45 * * * * dispara no início, um quarto de hora depois, na metade e três quartos de toda hora. A ordem não importa para o analisador, mas leitores apreciam listas em ordem crescente.
- — intervalo
O hífen define um intervalo inclusivo. 0 9-17 * * 1-5 dispara no início de cada hora das 09:00 às 17:00 inclusive, de segunda a sexta. Ambos os endpoints são incluídos; são nove execuções por dia, não oito.
/ — step
A barra aplica um step a um intervalo. */5 no campo de minutos é abreviação de 0-59/5— a cada cinco minutos começando do minuto 0. Você pode combinar o step com um intervalo explícito: 0-30/10dispara em 0, 10, 20, 30 e em nenhum outro lugar.
A armadilha: */N divide o intervalo completo, não a partir do horário atual do relógio. */7em minutos dispara em 0, 7, 14, 21, 28, 35, 42, 49, 56 — então 0 da próxima hora, um intervalo de quatro minutos. O cron não é um temporizador periódico.
? — sem valor específico (somente Quartz)
O caractere ? nãoé POSIX. Ele aparece no cron no estilo Quartz (e no AWS EventBridge, que herda a sintaxe do Quartz) e significa “sem valor específico — o outro campo de dia é autoritativo.” Você escreveria 0 9 ? * MON-FRI no EventBridge mas 0 9 * * MON-FRI no cron clássico.
Valores nomeados
Os meses aceitam JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC, os dias da semana aceitam SUN MON TUE WED THU FRI SAT. São insensíveis a maiúsculas na maioria das implementações, mas sensíveis em algumas; maiúsculas é a escolha segura. Valores nomeados não funcionam dentro de intervalos ou steps em todas as implementações —MON-FRI funciona no Vixie cron mas não em todas as variantes BSD. Em caso de dúvida, use a forma numérica.
Receitas
A maneira mais rápida de internalizar o cron é ler algumas dezenas de expressões ao lado do inglês simples. Cole qualquer uma delas em nosso construtor de expressões cron para ver os próximos dez horários de disparo na sua zona local.
A cada N minutos
* * * * *— todo minuto*/5 * * * *— a cada cinco minutos, no minuto 0, 5, 10…*/15 * * * *— a cada quarto de hora0,30 * * * *— na hora e na meia hora5,35 * * * *— com offset de cinco minutos — útil para escalonar jobs que competem pelo mesmo banco de dados
Variantes horárias
0 * * * *— toda hora no início0 */2 * * *— a cada duas horas começando às 00:000 9-17 * * *— no início da hora, das 09:00 às 17:00 inclusive
Variantes diárias
0 0 * * *— meia-noite todo dia (equivalente a@daily)30 3 * * *— 03:30 todo dia — a janela clássica de backup0 9 * * 1-5— 09:00 em dias úteis0 18 * * 5— 18:00 somente nas sextas-feiras
Semanais, mensais, anuais
0 9 * * 1— 09:00 toda segunda-feira (equivalente ao espírito de@weeklymas com horário sensato)0 0 1 * *— meia-noite no primeiro de todo mês (equivalente a@monthly)0 0 1 1 *— meia-noite em 1º de janeiro (equivalente a@yearly)0 0 28-31 * *— meia-noite nos últimos quatro dias possíveis do mês, combinado com um script que verifica “é este o último dia?” — o cron não tem “último dia do mês” nativo.
Horário comercial
0 9-17 * * 1-5— no início da hora, 09:00-17:00, segunda a sexta — nove disparos por dia útil*/10 9-17 * * 1-5— a cada dez minutos durante o horário comercial0 9,13,17 * * 1-5— abertura, pós-almoço, encerramento — três disparos por dia útil
A peculiaridade OR de dia-do-mês / dia-da-semana
Este é o comportamento mais perguntado no cron, e a especificação é inequívoca: se ambos o campo dia-do-mês e o campo dia-da-semana forem restritos (isto é, não *), o job dispara quando qualquer condição corresponder, não ambas.
Considere 0 0 1 * 1. Uma leitura natural é “meia-noite no primeiro do mês, mas só se for segunda.” O comportamento real é “meia-noite no primeiro de todo mês, oumeia-noite toda segunda.” Isso são aproximadamente cinco disparos por mês, não o zero a um que você provavelmente pretendia.
A solução: deixe um dos dois campos de dia como *. Se você genuinamente precisar de semântica AND — “primeira segunda do mês” — você terá que verificar dentro do script:
# crontab: disparar meia-noite nos primeiros sete dias do mês
0 0 1-7 * * /usr/local/bin/talvez-executar-mensal.sh
# dentro de talvez-executar-mensal.sh: sair se hoje não for segunda
[ "$(date +%u)" = "1" ] || exit 0O cron no estilo Quartz (e o EventBridge) contorna a peculiaridade com o caractere ? e tokens explícitos de nth-weekday-of-month como MON#1. O cron clássico não tem nenhum dos dois.
Fusos horários e horário de verão
As expressões cron são avaliadas no fuso horário local do agendador. No Linux, é para o que /etc/localtime aponta; no macOS, é a preferência do sistema. A expressão em si nunca nomeia uma zona.
Isso torna as transições de horário de verão interessantes. Em uma zona que observa o horário de verão, o relógio local avança (uma hora ignorada) na primavera e retrocede (uma hora repetida) no outono. Um job cron em 0 2 * * *pode não disparar no dia do avanço — a hora das 02:00 não existiu — e pode disparar duas vezes no outono.
A maioria dos agendadores em nuvem aceita um fuso horário explícito por regra; identificadores de zona IANA como America/New_York são corretos, EST não é (não observa horário de verão). Se precisar de consistência absoluta, agende entre 03:00 e 23:00 (bem longe de qualquer janela de horário de verão) ou execute o agendador em UTC e lide com a aritmética de horário local.
Erros comuns
Tratar a barra como um período verdadeiro
Cobrimos isso acima. */N divide o intervalo do campo, então o período entre o último disparo de uma hora e o primeiro da próxima é menor que N. Se precisar de espaçamento estrito de N minutos, o cron é a ferramenta errada.
Esquecer que o minuto 0 existe
0-59/15dispara em 0, 15, 30, 45 — quatro vezes por hora, não três. O intervalo começa em 0. Bugs de off-by-one são raros no cron porque cada campo tem endpoints bem definidos, mas este aparece quando as pessoas traduzem do inglês humano (“quatro vezes por hora”) para expressão e esquecem o minuto zero implícito.
Colocar um comentário após o comando
O cron trata toda a linha após o quinto campo como um único comando. # comentário em uma linha de crontab faz parte do comando do shell, o que geralmente apenas expande para nada porque #inicia um comentário no shell — mas se estiver dentro de uma string entre aspas, você alterou seu comando. Coloque comentários em sua própria linha acima do agendamento.
Assumir o PATH do job
O cron é executado com um ambiente mínimo. PATHé tipicamente /usr/bin:/bin e seus aliases de shell não existem. Use caminhos absolutos para todos os binários ou defina PATH=no topo do crontab. Se um job “funciona quando executo manualmente mas não pelo cron,” esta é quase sempre a causa.
Deixar o stdout encher a caixa de entrada
O cron envia por email a saída de cada job ao usuário local por padrão. Em um servidor sem e-mail configurado, o arquivo de spool cresce silenciosamente até que outra coisa quebre. Acrescente >>/var/log/meujob.log 2>&1 a cada comando e faça rotação do log.
Cartão de referência rápida
| Expressão | Significado |
|---|---|
* * * * * | todo minuto |
*/5 * * * * | a cada 5 minutos |
0 * * * * | início de toda hora |
0 9-17 * * 1-5 | horário, 9-17, dias úteis |
0 0 * * * | meia-noite diária |
30 3 * * 0 | 03:30 todo domingo |
0 0 1 * * | meia-noite no dia 1 de cada mês |
0 0 1 1 * | meia-noite em 1º de janeiro |
@reboot | uma vez ao iniciar o agendador (dependente do sistema) |
Verifique antes de confirmar
A maneira mais barata de evitar um alerta às 3h da manhã é colar sua expressão em um analisador antes de salvar o crontab. Nosso construtor de expressões cron imprime os próximos dez horários de disparo na sua zona local e em UTC, além de uma descrição em linguagem simples que pega a peculiaridade dia-do-mês / dia-da-semana na hora.
O cron é uma ferramenta pequena e precisa. Leia a especificação uma vez, construa memória muscular para os cinco campos, cole novas expressões em um analisador até parecerem obviamente corretas — e leve em conta que o modo de falha de uma expressão mal lida geralmente é silencioso, não ruidoso.
Frequently asked questions
- O cron usa o fuso horário do servidor ou UTC?
- O cron Unix clássico usa o fuso horário local do sistema, que é qualquer coisa para a qual /etc/localtime aponta. Muitos agendadores em nuvem (AWS EventBridge, GCP Cloud Scheduler) usam UTC por padrão e permitem optar por uma zona IANA nomeada por regra. Sempre imprima o fuso horário resolvido na primeira linha de log do seu job — isso elimina toda uma categoria de bugs pós-horário de verão.
- Por que meu job rodou duas vezes na manhã em que o horário de verão terminou?
- Quando os relógios retrocedem de 02:00 para 01:00, a hora entre 01:00 e 02:00 acontece duas vezes no relógio local. Um job agendado às 01:30 em uma zona com horário de verão roda nos dois momentos, a menos que sua implementação cron elimine duplicatas explicitamente. A solução é agendar entre 03:00 e 23:00 (nunca dentro da janela ambígua) ou executar o agendador em UTC.
- Qual é o menor intervalo que o cron suporta?
- Um minuto. O primeiro campo é minutos; não há campo de segundos no crontab POSIX. Se precisar de agendamento abaixo de um minuto, use um processo de longa duração com seu próprio loop de sleep, temporizadores systemd com OnUnitActiveSec ou um agendador dedicado como o Quartz que suporta um campo de segundos.
- @hourly, @daily, @reboot são portáveis?
- As strings abreviadas (@yearly, @monthly, @weekly, @daily, @hourly) são suportadas pelo Vixie cron, cronie e pela maioria dos derivados modernos, então são seguras no Linux e macOS. @reboot é amplamente suportado, mas sua semântica difere — algumas implementações rodam uma vez na próxima inicialização, outras em cada inicialização. Não use @reboot em contêineres; use o próprio comando de início do contêiner.
- Por que */7 às vezes pula o último intervalo?
- O operador step divide o intervalo permitido inteiro, não o horário de início. Para minutos (0-59), */7 dispara em 0, 7, 14, 21, 28, 35, 42, 49, 56 — depois o próximo minuto válido é 0 da próxima hora. O intervalo entre 56 e 60 é de apenas quatro minutos, não sete. Se precisar de um período estrito de sete minutos, use um agendador de longa duração com um temporizador de intervalo real, não cron.
- Devo usar cron em 2026 ou temporizadores systemd?
- Em um único host Linux sem orquestrador, os temporizadores systemd geralmente são um padrão melhor: registro estruturado via journalctl, ordenação de dependências, randomização mais fácil com RandomizedDelaySec e limites de recursos por unidade. O cron vence quando você precisa de portabilidade em sistemas sem systemd ou quando toda a automação é uma linha no crontab. Em ambientes orquestrados por contêineres, nenhum dos dois — use o agendador nativo da plataforma (Kubernetes CronJob, ECS Scheduled Tasks etc.).
Related
- Construtor e explicador de expressões cronCole uma expressão, veja as próximas execuções e uma descrição em linguagem simples
- Glossário de fusos horários IANAPor que as zonas são escritas America/New_York, não EST
- Glossário de timestamp UnixSegundos desde 1970-01-01 UTC — a base de quase todo agendador
- Conversor de timestampConverter entre segundos epoch, ISO 8601 e seu fuso horário local
- Guia de fusos horários para equipes remotasMatemática prática de fusos para agendamento distribuído
Published May 31, 2026