Uso básico
Cada usuário tem um crontab, e os comandos dele são executados com a identidade desse usuário. As operações comuns são apenas quatro:
crontab -e
Edita o crontab do usuário atual; ao sair do editor, a modificação é instalada automaticamente.
crontab -l
Lista o crontab do usuário atual.
crontab -r
Apaga o crontab do usuário atual. Depois de apagar, crontab -l mostra no crontab for root (usando root como exemplo). Faça um backup antes de apagar: crontab -l > crontab.bak.
crontab -n arquivo
Execução de teste: apenas verifica a sintaxe desse arquivo e imprime uma mensagem de sucesso, sem gravar em nenhum crontab.
Os arquivos de crontab ficam de fato em /var/spool/cron/crontabs, e você não deve editar diretamente os arquivos desse diretório; use o comando crontab.
Ao operar em um ambiente com su para outro usuário, o manual recomenda sempre usar -u user para especificar explicitamente de qual usuário é o crontab, porque o su pode fazer o crontab se confundir sobre o usuário atual.
Quanto a permissões: se /etc/cron.allow existir, apenas os usuários listados nele podem usar o crontab; se ele não existir e /etc/cron.deny existir, os usuários listados no deny não podem usar; se ambos existirem, o allow prevalece. root é sempre permitido.
Os comandos e saídas deste artigo vêm das páginas de manual do cron 3.0pl1-200ubuntu1 no Ubuntu 26.04.1, além de testes que fiz em um contêiner desse sistema (usuário root). Outras distribuições podem ser diferentes; em caso de dúvida, confira com man 5 crontab.
Os cinco campos de tempo
Uma linha válida em um crontab é de apenas dois tipos: definição de variável de ambiente (name = value, com espaços opcionais ao redor do sinal de igual) ou comando cron. A linha de comando tem cinco campos de data e hora, separados por espaços ou tabulações, e o campo de comando tem no máximo 998 caracteres.
Os cinco campos, da esquerda para a direita:
minuto hora dia mês dia-da-semana
0-59 0-23 0-31 0-12 0-7
No campo do dia da semana, tanto 0 quanto 7 significam domingo. O cron verifica uma vez por minuto e executa o comando quando os campos de minuto, hora e mês correspondem ao horário atual e pelo menos um dos campos "dia" e "dia da semana" corresponde. Esse detalhe de "pelo menos um" é tratado mais adiante em seção própria.
Regras de escrita:
*significa "do primeiro ao último", ou seja, qualquer valor.- Intervalos usam hífen, com extremos incluídos:
8-11significa 8, 9, 10 e 11 horas. - Listas usam vírgula:
1,2,5,9,0-4,8-12. - Passos usam
/, podendo vir depois de um intervalo ou de um asterisco:0-23/2significa a cada duas horas, e*/2tem o mesmo efeito. - Mês e dia da semana aceitam as três primeiras letras do nome em inglês (sem distinção entre maiúsculas e minúsculas), mas os nomes só podem ser usados sozinhos, não dentro de intervalos ou listas.
Alguns exemplos do manual:
5 0 * * * $HOME/bin/daily.job >> $HOME/tmp/out 2>&1
Executa todo dia às 0:05.
15 14 1 * * $HOME/bin/monthly
Todo dia 1 do mês, às 14:15.
0 22 * * 1-5
Dias úteis (segunda a sexta) às 22h.
23 0-23/2 * * *
Todo dia às 0:23, 2:23, 4:23 e assim por diante.
Sintaxes especiais
Oito strings especiais podem substituir os cinco primeiros campos:
@reboot: executa uma vez na inicialização.@yearly,@annually: equivale a0 0 1 1 *.@monthly: equivale a0 0 1 * *.@weekly: equivale a0 0 * * 0.@daily,@midnight: equivale a0 0 * * *.@hourly: equivale a0 * * * *.
@reboot tem uma questão de momento a observar: "inicialização" refere-se ao instante em que o daemon do cron inicia, o que pode ser antes de certos daemons do sistema ou de outras estruturas. Se o serviço do qual seu script depende ainda não subiu, o @reboot vai falhar ao ser disparado. Nesse caso, adicione retry ou atraso ao comando.
Variáveis de ambiente e PATH
O cron define automaticamente algumas variáveis: SHELL como /usr/bin/sh (é o que diz o manual; no ambiente testado era /bin/sh), e LOGNAME e HOME retirados da linha do dono do crontab em /etc/passwd. HOME e SHELL podem ser sobrescritos no crontab; LOGNAME, não.
O que mais causa problemas é o PATH. No contêiner do ambiente de teste citado acima, instalei uma tarefa * * * * * env > /tmp/cronenv; o ambiente com que o cron de fato executa era: HOME=/root, LOGNAME=root, PATH=/usr/bin:/bin, PWD=/root, SHELL=/bin/sh.
O PATH de uma tarefa do cron tem apenas /usr/bin:/bin (resultado deste ambiente de teste); /usr/local/bin, /sbin e /usr/sbin não estão nele. Portanto, ferramentas que você instalou em /usr/local/bin ou comandos do sistema em /sbin não são encontrados se você escrever só o nome no cron. Duas soluções: usar caminhos absolutos, ou adicionar uma linha de PATH no início do crontab. Para saber qual é exatamente o PATH no seu ambiente, instale uma tarefa * * * * * env > /tmp/cronenv e veja por conta própria.
Há ainda uma armadilha sutil: os valores das variáveis de ambiente no crontab não passam por substituição de variáveis nem por expansão de til. PATH=$HOME/bin:$PATH e PATH=~/bin:/usr/bin não funcionam como você espera; depois de A=1 e B=2, escrever C=$A $B também não substitui. Para adicionar diretórios ao PATH, escreva o caminho completo.
MAILTO controla para quem a saída é enviada: se definido e não vazio, vai para o usuário indicado; se definido como vazio (MAILTO=""), nenhum e-mail é enviado; se não for definido, vai para o dono do crontab. Para deixar a tarefa silenciosa, redirecionar para um arquivo ou para /dev/null é mais direto.
A armadilha do percentual
A parte do comando é executada por /usr/bin/sh ou pelo shell indicado via SHELL no crontab. Um sinal de percentual % no comando, a menos que esteja escapado com barra invertida, é convertido em quebra de linha, e todo o conteúdo depois do primeiro % é passado ao comando como entrada padrão.
O teste mostra bem o problema. Esta tarefa, no contêiner, simplesmente não gerou o arquivo de log:
* * * * * date +%s >> /tmp/cronout
Como o % foi tratado como quebra de linha, o comando foi truncado em date +. Só funciona depois de escapar:
* * * * * date +\%s >> /tmp/cronout 2>&1
O arquivo passa a receber uma linha com o timestamp Unix. Portanto, date +%F, tar com strings de formato, qualquer comando com % precisa ser escrito como \% no crontab.
Comentário na mesma linha não é comentário
Um comentário escrito na mesma linha de um comando cron não é tratado como comentário no sentido do cron: ele faz parte do comando e é entregue ao shell; o mesmo vale para linhas de definição de variável de ambiente. Para escrever um comentário, use uma linha própria, começando com #.
Restringir dia e dia da semana ao mesmo tempo é "ou"
Esta é a semântica mais contraintuitiva do crontab: quando os campos "dia" e "dia da semana" estão ambos restritos (nenhum é *), a condição de correspondência é o "ou" entre os dois, não o "e".
O exemplo do manual: 30 4 1,15 * 5 executa às 4:30 nos dias 1 e 15 de cada mês e, além disso, às 4:30 de toda sexta-feira — e não "nas sextas-feiras que caem nos dias 1 e 15 do mês".
Para obter o efeito de "e", o manual sugere incluir um teste no comando, por exemplo o segundo sábado do mês:
0 4 8-14 * * test $(date +\%u) -eq 6 && echo "2nd Saturday"
Note que aqui o %u precisa ser escrito como \%u — é a armadilha da seção anterior.
crontab de sistema e /etc/cron.d
O /etc/crontab e os arquivos sob /etc/cron.d são configurações de nível de sistema, com o mesmo formato do crontab de usuário, exceto por um campo adicional de nome de usuário entre os cinco campos de tempo e o comando. Modificar esses arquivos não exige rodar o comando crontab para instalar, mas editá-los requer permissão de root (por exemplo, abrir o editor com sudo).
No ambiente de teste, /etc/cron.d trazia entradas da própria distribuição (como e2scrub_all), e /etc/cron.daily tinha apt-compat e dpkg. No /etc/crontab, SHELL é /bin/sh, com um comentário explicando que o PATH pode ser sobrescrito.
Checklist de diagnóstico
Quando a tarefa não roda ou o resultado está errado, verifique nesta ordem:
crontab -lpara confirmar que a entrada foi mesmo instalada; se você tiver o arquivo original, usecrontab -n arquivopara checar a sintaxe — esse comando apenas verifica, não instala.- Se todos os
%no comando foram escritos como\%. - Se o comando depende de executáveis fora do PATH do cron (no ambiente testado neste artigo,
/usr/bin:/bin). Tente caminhos absolutos. - Se o script depende de expansão de
$HOME, til e construções do tipo. - Se dia e dia da semana estão restritos ao mesmo tempo — você pode ter caído na semântica de "ou".
- Se algum comentário foi escrito na mesma linha do comando.
- Se uma tarefa
@rebootfalhou, considere que o cron pode ter iniciado antes dos serviços dos quais ela depende.
systemd timer como alternativa
As unidades timer do systemd também fazem execução agendada e, em relação ao crontab, têm duas coisas a mais: as expressões de calendário podem ser pré-visualizadas na linha de comando quanto à próxima execução; e a opção Persistent= permite reexecutar disparos perdidos.
Primeiro, a pré-visualização. systemd-analyze calendar calcula offline a próxima execução de uma expressão de calendário, sem criar nada, e funciona até em contêineres sem systemd em execução:
systemd-analyze calendar 'Mon..Fri *-*-* 09:00'
A saída inclui as linhas Normalized form: (forma normalizada) e Next elapse: (próxima execução). Alguns resultados reais: a forma normalizada de Mon..Fri *-*-* 09:00 é Mon..Fri *-*-* 09:00:00; a próxima execução de *-*-* 03:20:00 é Mon 2026-10-19 03:20:00 UTC; weekly se normaliza para Mon *-*-* 00:00:00; *:0/15 significa a cada 15 minutos. A sintaxe das expressões está detalhada em systemd.time(7).
Um timer precisa vir acompanhado de um service. Um exemplo realmente implantado neste site (uma seção [Timer]):
[Timer]
OnCalendar=*-*-* 03:20:00 Asia/Shanghai
Persistent=false
AccuracySec=1min
Unit=alguma-tarefa.service
A expressão pode vir seguida diretamente de um fuso horário. O service correspondente é Type=oneshot, ou seja, execução única.
Persistent= tem como padrão false. Quando definido como true, o horário da última execução do serviço é gravado em disco; ao ser ativado, se pelo menos uma execução deveria ter ocorrido durante o período em que ficou parado, o serviço é disparado imediatamente. Isso resolve exatamente o caso de "reexecutar tarefas perdidas enquanto a máquina estava desligada", como backups e limpeza de logs. Só vale para timers configurados com OnCalendar=. O manual ainda alerta: antes de desinstalar um timer, use systemctl clean --what=state nome-do-timer.timer para apagar o timestamp que ele guardou em disco, substituindo o nome pela sua unidade timer real; caso contrário, reinstalar um timer com o mesmo nome pode ler o estado antigo.
Outro ponto a saber: o timer não necessariamente dispara no instante exato configurado em OnCalendar=, por causa da janela de precisão de opções como AccuracySec=.
Como escolher
O que segue é minha recomendação, não uma conclusão oficial. Para tarefas simples, em que perder uma execução não importa e a máquina fica sempre ligada, o crontab é o mais rápido de escrever, resolvido em cinco minutos. Considere o systemd timer em qualquer um destes casos: a perda de uma execução precisa ser compensada (use Persistent=true); a regra de tempo é complexa e você quer validar a expressão antes na linha de comando; você já usa o systemd para gerenciar unidades de serviço semelhantes e quer que as tarefas agendadas sejam gerenciadas da mesma forma. Os dois métodos podem coexistir; decida tarefa por tarefa.
Leitura complementar
man 5 crontab, man 1 crontab e man systemd.timer são a fonte de todas as conclusões deste artigo.




