Cron: o agendador de tarefas do Unix

Criado no Bell Labs no início dos anos 1970, o cron é um daemon Unix responsável por executar comandos em horários pré-determinados. Não há interface gráfica, não há serviço de nuvem por trás, não há SDK para instalar. Existe uma tabela de texto, um conjunto de regras simples, e um processo que acorda uma vez por minuto para verificar o que precisa ser executado. Em 2026, essa mesma lógica ainda roda em milhões de servidores.

A primeira versão do cron foi escrita por Ken Thompson para o Version 7 Unix, em 1977. O funcionamento era direto: o daemon acordava a cada minuto, lia um único arquivo de configuração em /etc/lib/crontab, e executava os comandos agendados para aquele momento, sempre como superusuário. Sem suporte a múltiplos usuários, sem extensões de sintaxe. Apenas o essencial.

Ao longo da década seguinte, o cron foi portado para várias distribuições Unix sem grandes mudanças. A virada veio em 1987, quando Paul Vixie, depois de consultar a comunidade Unix sobre o que faltava na ferramenta, publicou uma nova versão do daemon. O Vixie cron adicionou suporte a crontabs individuais por usuário, expandiu a sintaxe de expressões e resolveu problemas acumulados nas implementações anteriores. Em 1992, a versão 3 do Vixie cron foi publicada na lista comp.sources.unix e tornou-se o ancestral comum de praticamente todas as variantes que existem hoje.

Red Hat e SUSE mantêm desde 2007 um fork chamado cronie, que adiciona suporte a PAM e SELinux. Debian e Ubuntu ainda usam o código original de Vixie com patches acumulados. São caminhos diferentes, mas todos derivam do mesmo tronco.

Em 2025, a Open Cron Pattern Specification (OCPS 1.0) foi publicada como uma tentativa de formalizar o dialeto Vixie cron e resolver ambiguidades que ficaram abertas por décadas. É um sinal de que o formato ainda importa o suficiente para merecer uma especificação escrita.

O que o cron faz

O cron é um daemon, um processo que roda em background de forma contínua. Ele não executa uma tarefa específica, ele executa o que o administrador mandar, no horário que o administrador definir.

Casos de uso comuns incluem:

  • Backups noturnos de banco de dados
  • Geração e envio de relatórios periódicos
  • Limpeza de arquivos temporários e logs antigos
  • Sincronização de dados entre sistemas
  • Renovação automática de certificados SSL
  • Monitoramento e healthchecks recorrentes
  • Rotação de chaves e tokens

Qualquer coisa que precise acontecer em intervalos regulares, sem que alguém precise estar ali para acionar manualmente, é candidata a virar um cron job.

A estrutura do crontab

As tarefas do cron ficam registradas em arquivos chamados crontabs. Cada linha representa um job e segue um formato fixo:

* * * * * comando_a_executar

Os cinco campos antes do comando representam, nessa ordem: minuto, hora, dia do mês, mês e dia da semana. Uma forma de lembrar é pensar da unidade menor para a maior, do detalhe para o contexto.

* * * * * comando
│ │ │ │ │
│ │ │ │ └── dia da semana  (0-6, onde 0 = domingo e 6 = sábado)
│ │ │ └──── mês            (1-12)
│ │ └────── dia do mês     (1-31)
│ └──────── hora           (0-23)
└────────── minuto         (0-59)

O asterisco funciona como wildcard: significa "qualquer valor válido nesse campo". Uma linha com cinco asteriscos antes do comando executaria aquele comando todo minuto, todos os dias, o ano inteiro.

A sintaxe em detalhe

A força do cron está na composição de quatro operadores básicos que podem ser combinados livremente em cada campo.

Valor único

O caso mais simples. Um número inteiro dentro do range permitido.

0 9 * * * /usr/bin/relatorio.sh
Executa às 9h00 exatas, todos os dias.

O campo de minuto é 0, o campo de hora é 9, os outros três são wildcard.

Lista

Múltiplos valores separados por vírgula. O job executa quando qualquer valor da lista corresponde ao momento atual.

0 9 * * 1,3,5 /usr/bin/relatorio.sh
Executa às 9h00 nas segundas (1), quartas (3) e sextas (5).

Range

Dois valores separados por hífen, representando um intervalo inclusivo.

0 9-17 * * 1-5 /usr/bin/healthcheck.sh
Executa no minuto 0 de cada hora entre 9h e 17h, de segunda a sexta.

Step

Uma barra seguida de um número, que define o passo entre valores. */N significa "a cada N unidades", começando do valor mínimo do campo.

*/15 * * * * /usr/bin/sync.sh
Executa nos minutos 0, 15, 30 e 45 de cada hora (de 15 em 15 minutos).

Equivale a 0,15,30,45 * * * *.

O step pode ser combinado com range:

0 8-18/2 * * * /usr/bin/tarefa.sh
Executa a cada 2 horas entre 8h e 18h: 8h, 10h, 12h, 14h, 16h, 18h.

Combinações

Os operadores podem ser misturados dentro de um mesmo campo:

0,30 9-17 * * 1-5 /usr/bin/tarefa.sh
Executa no minuto 0 e no minuto 30, entre 9h e 17h, de segunda a sexta.

Oito execuções por dia útil.

Nomes por extenso

Os campos de mês e dia da semana aceitam abreviações em inglês no lugar de números. MON, TUE, WED, THU, FRI, SAT, SUN para dias da semana; JAN a DEC para meses. Isso melhora a legibilidade em expressões com ranges:

0 0 * JAN-MAR 1-5 /usr/bin/relatorio-trimestral.sh
Executa à meia-noite de segunda a sexta, durante janeiro, fevereiro e março.

Gerenciando crontabs

Para editar os jobs do usuário atual, o comando é:

crontab -e

Isso abre o crontab no editor definido pela variável $EDITOR. Para listar os jobs existentes:

crontab -l

Para remover todos os jobs do usuário:

crontab -r

O crontab do sistema fica em /etc/crontab e em arquivos dentro de /etc/cron.d/. Diferente dos crontabs de usuário, esses arquivos têm um sexto campo entre a expressão e o comando, indicando qual usuário deve executar o job:

0 2 * * * root /usr/bin/backup.sh

Também existem os diretórios /etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly e /etc/cron.monthly. Scripts colocados nesses diretórios são executados automaticamente nos intervalos correspondentes pelo daemon, sem precisar escrever uma expressão manualmente.

Variáveis de ambiente no crontab

O ambiente em que o cron executa os comandos não é o mesmo do shell interativo. Variáveis como PATH, HOME e SHELL têm valores padrão mínimos, o que é causa frequente de falhas silenciosas quando um script funciona na linha de comando mas não no cron.

É possível definir variáveis diretamente no crontab, antes das linhas de jobs:

SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=[email protected]
 
0 2 * * * /usr/local/bin/backup.sh

A variável MAILTO define para qual endereço o cron envia a saída dos jobs. Se estiver vazia (MAILTO=""), nenhum e-mail é enviado. Isso é importante: por padrão, o cron tenta enviar um e-mail com qualquer saída gerada pelo job, o que pode encher a fila de mensagens local se o script gerar output com frequência.

Uma prática comum é redirecionar a saída para um arquivo de log ou para /dev/null:

# Ignora stdout e stderr
0 2 * * * /usr/local/bin/backup.sh > /dev/null 2>&1
 
# Salva stdout e stderr em um log
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

A armadilha do dia do mês com dia da semana

Um detalhe de comportamento que gera confusão: quando tanto o campo dia do mês quanto o campo dia da semana estão definidos com valores específicos (nenhum dos dois é wildcard), o Vixie cron os trata com semântica OR. O job executa se qualquer uma das condições for satisfeita, não se ambas forem verdadeiras ao mesmo tempo.

Por exemplo:

0 0 1 * 1 /usr/bin/tarefa.sh

Essa expressão executa à meia-noite no dia 1 de cada mês e também toda segunda-feira, não apenas nas segundas-feiras que caem no dia 1. Se a intenção era executar só nas segundas que fossem dia 1, o cron padrão não resolve diretamente. A solução convencional é usar um wildcard em um dos campos e colocar a lógica restante dentro do próprio script:

0 0 1 * * [ $(date +%u) -eq 1 ] && /usr/bin/tarefa.sh

Vale mencionar que o Quartz, o scheduler popular no ecossistema Java, usa semântica AND para esses campos e exige que um deles receba ? quando o outro estiver definido. São comportamentos incompatíveis, e trocar de implementação sem atenção a esse detalhe quebra agendamentos silenciosamente.

Limitações conhecidas

O cron é confiável para o que se propõe, mas tem limitações que valem ser conhecidas antes de depender dele em contextos críticos.

A ausência de feedback é a mais significativa. Quando um job falha, o cron não avisa por padrão, a não ser que o sistema de e-mail local esteja configurado e o MAILTO esteja definido. Um backup que retorna erro à meia-noite pode passar despercebido por semanas.

Outra limitação é a granularidade mínima de um minuto. Expressões com precisão de segundos não são suportadas na sintaxe padrão. Para tarefas que precisam rodar a cada 10 segundos, por exemplo, é necessário recorrer a scripts com sleep ou usar uma solução diferente.

O cron também não tem noção de dependência entre jobs. Se a tarefa A precisa ter terminado antes de B começar, isso precisa ser gerenciado externamente.

Por fim, em sistemas que ficam offline periodicamente, um job agendado durante o período de downtime simplesmente não roda. Para isso existe o anacron, que mantém registro das últimas execuções e garante que jobs com intervalos em dias sejam rodados assim que o sistema voltar, mesmo que fora do horário previsto.

Ferramentas para facilitar o trabalho

Escrever e entender expressões cron ficou mais fácil com a disponibilidade de editores visuais, para quem quer algo com visualização campo a campo e exibição das próximas datas de execução, o Cron Editor no ASCII Tools oferece um timeline visual de quais valores estão ativos em cada campo, além de validação por campo com mensagens de erro e presets para as expressões mais comuns.

Referências