SSD Nodes Learn Hosting plans →
Guias Matt ConnorPor Matt Connor · Atualizado 2026-09-04

Por que seu cron job não executa? Veja 5 causas

Descubra por que o cron falha: PATH mínimo, % sem escape, crontab errado, saída perdida no mail e script que depende do shell de login.

Por que o seu cron job não é executado

Um cron job que "nunca é executado" quase sempre foi executado. Ele foi executado num ambiente diferente do seu shell, falhou no primeiro segundo e a mensagem foi enviada para um local que não está a consultar. Cinco causas explicam quase todos os casos reportados: o caminho de pesquisa, o sinal de percentagem, o ficheiro crontab errado, a saída enviada por correio e um script que espera uma sessão de login.

cron é um daemon (um serviço em segundo plano) que lê ficheiros crontab e inicia comandos de acordo com uma agenda. Não lê o seu .bashrc, não abre um terminal, não inicia uma login shell e não informa quando um comando falha. Todas as causas abaixo resultam destes quatro factos.

Analise-as pela ordem apresentada e comece pela pergunta que está na base de todas elas: o cron foi acionado? "o cron nunca iniciou o job" e "o job foi iniciado e terminou" são problemas diferentes, sem nada em comum. Por isso, responda primeiro a essa pergunta.

O cron chegou a executar?

O daemon tem um nome de unidade diferente em cada família de distribuições. Verifique ambos e, em seguida, leia o log.

systemctl status cron
systemctl status crond
journalctl -u cron --since "2 hours ago"
journalctl -u crond --since "2 hours ago"

Debian e Ubuntu chamam a unidade de cron. Fedora, Rocky e Alma chamam-na de crond. Apenas um desses nomes existe numa determinada máquina, por isso é normal que um dos dois comandos indique uma unidade desconhecida. Isso não é uma falha.

Leia as entradas gravadas pelo seu próprio sistema. Não procure uma linha copiada de um guia, porque o texto varia entre implementações do cron e configurações de logging. Verifique apenas duas coisas: existe uma entrada no minuto indicado pelo seu agendamento e essa entrada identifica o seu comando. Uma entrada que identifica o seu comando significa que o cron cumpriu a sua parte e que a falha está dentro do comando. A ausência total de uma entrada significa que o cron nunca recebeu o seu agendamento. Essa é a causa 3 abaixo.

Algumas imagens enviam as mensagens do cron através do rsyslog para um ficheiro, em vez de as enviarem para o journal. Procure em /var/log um ficheiro com um nome relacionado com cron ou syslog e leia o fim desse ficheiro.

ls -l /var/log
sudo tail -n 50 /var/log/syslog

Se nem a unidade nem o log existirem, é possível que o cron simplesmente não esteja instalado. Imagens cloud mínimas e contentores frequentemente não o incluem.

dpkg -l cron
rpm -q cronie
sudo apt install cron
sudo dnf install cronie
sudo systemctl enable --now cron

Causa 1: o cron não tem o seu PATH

A sua shell interativa cria PATH a partir de /etc/profile, ~/.profile, ~/.bashrc e de tudo o que esses ficheiros carregam. Nada disso é executado para uma tarefa do cron. O cron inicia o comando com o seu próprio ambiente reduzido, por isso um programa localizado fora dos diretórios de sistema padrão não é encontrado. Tudo o que estiver em /usr/local/bin, /opt, num gestor de versões de linguagens, num ambiente virtual Python ou num workspace Go pode causar este problema. A tarefa falha na primeira linha, e a shell escreve um erro do tipo "not found", cuja redação exata depende da shell que a executou.

Encontre o caminho real de todos os comandos usados pela tarefa.

command -v docker
command -v node
readlink -f "$(command -v node)"

Em seguida, escreva esses caminhos absolutos na tarefa ou defina PATH uma vez no início do crontab.

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 3 * * * /usr/local/bin/mytool run

Obtenha essa lista na sua própria máquina com echo "$PATH" e remova tudo o que exista apenas numa sessão interativa. Há uma regra importante: o cron não expande variáveis nestas linhas de atribuição. PATH=$PATH:/usr/local/bin guarda o texto literal $PATH:/usr/local/bin, por isso a tarefa fica com um caminho de pesquisa que não contém nenhum diretório utilizável. Escreva a lista completa.

Um gestor de versões precisa de mais do que um caminho. nvm, pyenv, rbenv e asdf instalam uma função de shell ou um diretório de shims a partir do seu .bashrc, e uma tarefa do cron nunca lê esse ficheiro. Chame o binário versionado pelo caminho absoluto ou carregue o script de inicialização do gestor como a primeira linha do seu próprio script.

Causa 2: o sinal de percentagem termina o comando

No campo de comando de um crontab, % não é um carácter comum. O primeiro % não escapado termina o comando. Tudo o que vem depois é passado ao comando como entrada padrão, e cada % seguinte transforma-se numa nova linha. Esta é uma funcionalidade real do cron para fornecer entradas curtas a um programa. É também por isso que um nome de ficheiro com data é o exemplo clássico de uma entrada de crontab avariada.

Escreva 0 3 * * * /usr/bin/tar -czf /srv/backups/site-$(date +%F).tar.gz /srv/site e o tar nunca vê uma data formatada. O cron corta a linha no primeiro %. Assim, o shell recebe uma substituição de comando incompleta, e o resto da linha chega como entrada padrão. Escape cada sinal de percentagem com uma barra invertida.

0 3 * * * /usr/bin/tar -czf /srv/backups/site-$(date +\%F).tar.gz /srv/site

Duas camadas leem essa linha, por ordem. \% é uma regra do cron, aplicada pelo cron antes de iniciar qualquer processo. $(date +\%F) é a substituição de comandos, aplicada depois pelo shell iniciado pelo cron. O ponto essencial é saber qual camada interpreta cada carácter.

A prática mais segura é manter a lógica fora do crontab. Coloque-a num script, onde o sinal de percentagem não tem um significado especial.

#!/bin/bash
set -euo pipefail
stamp="$(date +%F)"
tar -czf "/srv/backups/site-${stamp}.tar.gz" /srv/site

A linha do crontab passa então a conter apenas um caminho e um redirecionamento. Um crontab que pode ser lido rapidamente também pode ser depurado rapidamente.

Causa 3: qual crontab foi editado?

Não existe uma única crontab. Existem vários ficheiros, com proprietários diferentes e quantidades de campos diferentes, e uma tarefa escrita no ficheiro errado fica invisível.

  • crontab -e edita a crontab do utilizador que executa o comando. sudo crontab -e edita a crontab do root. Duas pessoas a depurar o mesmo servidor acabam muitas vezes por ler dois ficheiros diferentes.
  • sudo crontab -l -u deploy lista a crontab de outro utilizador. É assim que confirma o que está efetivamente instalado para a conta que deve executar a tarefa.
  • /etc/crontab e todos os ficheiros em /etc/cron.d têm um campo adicional entre a agenda e o comando: o utilizador com que a tarefa será executada. Se colar uma linha de crontab de utilizador, com cinco campos, em /etc/cron.d, a primeira palavra do seu comando será interpretada como um nome de utilizador.
  • Os ficheiros em /etc/cron.d têm de ter nomes compostos por letras, dígitos, underscores e hífenes. Um ficheiro chamado backup.sh ou site.conf é ignorado apenas por causa do nome. Mude o nome para backup e verifique novamente o log.
  • Os ficheiros em /etc/cron.d devem pertencer ao root e não podem ter permissões de escrita para o grupo ou para outros utilizadores. ls -l /etc/cron.d mostra ambos os factos de uma só vez.
  • Os scripts colocados em /etc/cron.daily e nos diretórios equivalentes seguem a mesma regra de nomes e também têm de ter o bit de execução definido. A ausência do bit de execução faz com que sejam ignorados silenciosamente.
  • /etc/cron.allow e /etc/cron.deny determinam quem pode instalar uma crontab. Se algum deles existir no seu servidor, leia-o antes de assumir que o seu utilizador tem essa permissão.

Instale uma crontab de utilizador com o comando crontab em vez de editar manualmente o ficheiro de spool, porque crontab analisa o ficheiro antes de o instalar. Depois de guardar, leia o que o comando devolve. Se o ficheiro for recusado, a versão anterior continua ativa e a sua alteração nunca entra em vigor. Isto parece exatamente que o cron está a ignorar o utilizador.

O proprietário também determina as permissões. Uma tarefa na crontab do root cria ficheiros pertencentes ao root que a aplicação que os lê pode não conseguir alterar. Uma tarefa na crontab de um utilizador normal não consegue ler um diretório acessível apenas ao root. Faça corresponder o proprietário ao trabalho: a manutenção da aplicação deve pertencer à própria conta da aplicação. Este é o motivo de substituir o wp-cron do WordPress por uma tarefa do cron do sistema. O modo dos ficheiros criados pela tarefa vem do umask que ela herda. Esse valor também pode ser diferente do umask da sua shell. Por isso, vale a pena ler como o umask define as permissões dos ficheiros quando a saída de uma tarefa fica sem permissões de leitura.

Causa 4: a saída foi enviada para uma caixa de correio que ninguém lê

O cron recolhe tudo o que um job escreve na saída padrão e no erro padrão. Se o job escrever qualquer coisa, o cron entrega esse texto ao sistema de correio local, endereçado ao proprietário do crontab ou ao que MAILTO indicar. Num VPS minimalista, normalmente não existe nenhum MTA (mail transfer agent) instalado, por isso nada entrega essa mensagem. O erro existiu durante um momento e depois não chegou a lado nenhum. É por isso que um job com problemas parece silencioso.

Envie a saída para um ficheiro que controla.

0 3 * * * /usr/local/sbin/backup-site.sh >> /var/log/backup-site.log 2>&1

>> acrescenta a saída padrão ao ficheiro. 2>&1 direciona o erro padrão para o destino atual da saída padrão, por isso tem de aparecer depois do redirecionamento. Se escrever pela ordem inversa, como 2>&1 >> file, o erro padrão mantém o seu destino original, e o erro que está a investigar é precisamente a parte que nunca chega ao ficheiro.

O journal é o outro destino adequado. logger escreve no syslog com uma tag à sua escolha.

0 3 * * * /usr/local/sbin/backup-site.sh 2>&1 | logger -t backup-site

Leia o conteúdo novamente com journalctl -t backup-site. Assim, a saída do próprio job fica junto das entradas do cron, o que facilita acompanhar a sequência temporal. Se também precisar de registar qual pessoa executou cada comando no servidor, esse é um sistema separado. Consulte auditoria dos comandos dos utilizadores no servidor.

MAILTO="" no início de um crontab desativa o correio para os jobs abaixo dessa linha. Definir MAILTO com um endereço real só ajuda se existir um MTA funcional. Por isso, confirme que o correio sai do servidor antes de depender dele.

Uma regra durante a depuração: nunca acrescente > /dev/null 2>&1. É a linha mais comum em todos os crontabs e elimina a única evidência disponível. Volte a adicioná-la mais tarde, se quiser, depois de o job funcionar.

Causa 5: o script pressupõe um ambiente que o cron não fornece

Depois de localizar o comando e capturar a respetiva saída, falta considerar tudo o que a sua sessão lhe fornece automaticamente.

  • A shell pode não ser bash. Verifique com ls -l /bin/sh. No Debian e no Ubuntu, esse comando aponta para dash, pelo que o teste com colchetes duplos, os arrays e source falham com um erro de sintaxe. Dê ao script uma linha #!/bin/bash e execute o script, ou defina SHELL no início do crontab.
  • O diretório de trabalho não é aquele em que se encontrava. Use caminhos absolutos em todo o lado ou cd para o diretório na primeira linha do script. Um caminho relativo é a causa isolada mais comum para um job que "funciona quando o executo manualmente".
  • O locale não é o da sua sessão. Qualquer comando que formate uma data ou um número, ou ordene texto, pode produzir uma saída diferente com outro LANG. Se uma etapa posterior analisar essa saída, defina o locale no script em vez de depender do valor existente.
  • Não existe TTY (terminal). Um comando que peça confirmação, abra um editor ou apresente uma barra de progresso pode ficar bloqueado ou terminar. Adicione o flag não interativo disponibilizado pela ferramenta.
  • Não existe um agente SSH. SSH_AUTH_SOCK não está no ambiente do cron, pelo que um comando ssh ou rsync que funcionava porque o seu agente estava carregado deixa de conseguir autenticar. Dê ao job a sua própria chave, pertencente ao utilizador que executa o job.
  • Não existe um bus de sessão do utilizador, pelo que systemctl --user falha quando é executado a partir de um job do cron até XDG_RUNTIME_DIR ser definido. Uma unidade do sistema é a solução mais adequada.

No Fedora, Rocky e Alma existe mais uma possibilidade. O SELinux confina os jobs do cron, pelo que um job que aceda a um caminho com um label inesperado é bloqueado, mesmo quando as permissões do ficheiro parecem corretas. Verifique as negações com sudo ausearch -m avc -ts recent e leia Noções básicas de SELinux para um servidor antes de desativar qualquer componente.

A verificação de um minuto que mostra o ambiente do cron

Pare de tentar adivinhar o que existe no ambiente do cron e leia-o. Escreva um script que descarregue tudo, agende-o para executar a cada minuto, aguarde e depois leia o ficheiro.

cat > /home/deploy/cron-probe.sh <<'EOF'
#!/bin/bash
echo "=== probe ==="
date -Is
pwd
id
echo "SHELL=$SHELL"
echo "LANG=$LANG"
command -v node || echo "node is not on this PATH"
env | sort
EOF
chmod +x /home/deploy/cron-probe.sh

Adicione uma linha ao crontab do utilizador com que o trabalho real é executado, usando caminhos absolutos em ambos os lados.

* * * * * /home/deploy/cron-probe.sh >> /home/deploy/cron-probe.log 2>&1

Aguarde um minuto e depois leia /home/deploy/cron-probe.log. Compare-o com os mesmos comandos executados na sua própria shell. A linha PATH, o diretório de trabalho e a locale normalmente explicam o problema por si só. Tenha em atenção dois detalhes da configuração: os sinais de percentagem estão dentro do script, onde a regra do cron não se aplica, e o caminho do log é um caminho em que o utilizador do trabalho pode escrever.

Apague essa linha do crontab assim que obtiver a resposta. Um trabalho que executa a cada minuto e acrescenta dados a um ficheiro pode encher um disco pequeno, e fará isso silenciosamente.

O agendamento é o que pretendia?

Uma linha de crontab de utilizador começa com cinco campos: minuto, hora, dia do mês, mês e dia da semana. Dois desses campos interagem de uma forma que surpreende muitas pessoas.

Quando o dia do mês e o dia da semana estão ambos restringidos, ou seja, nenhum deles é *, o cron executa o trabalho quando qualquer um dos campos corresponde. 0 0 13 * 5 não significa "sexta-feira 13". Executa o trabalho à meia-noite no dia 13 de todos os meses e à meia-noite de todas as sextas-feiras. Para obter um único dia específico, deixe um dos dois campos como * e teste o outro dentro do script.

O cron usa o fuso horário do sistema. Muitas imagens de VPS são fornecidas configuradas para UTC (tempo universal coordenado). Por isso, um trabalho agendado para as 03:00 é executado às 03:00 UTC, o que pode corresponder ao meio da tarde no seu fuso horário. timedatectl mostra o fuso horário efetivamente utilizado pelo seu sistema. Consulte esse valor em vez de assumir que coincide com o do seu portátil.

Também é importante conhecer mais duas armadilhas dos agendamentos. @reboot é acionado quando o próprio cron inicia. Isso não ocorre necessariamente quando a rede já está disponível. Por isso, um trabalho que precise de DNS ou de um host remoto pode falhar no arranque e funcionar em todas as execuções manuais seguintes. Além disso, nada impede que um trabalho lento seja iniciado novamente enquanto a execução anterior ainda está em curso. Envolva-o num bloqueio.

*/5 * * * * /usr/bin/flock -n /tmp/backup-site.lock /usr/local/sbin/backup-site.sh >> /var/log/backup-site.log 2>&1

flock -n termina imediatamente quando o bloqueio já está ocupado. Assim, a execução sobreposta é interrompida em vez de se acumular com a primeira.

Quando um temporizador do systemd é a ferramenta mais adequada

O cron é bom numa única tarefa: executar este comando a esta hora. É fraco em quase todo o resto. Um temporizador fornece o journal sem redirecionamentos, um estado de saída que pode consultar mais tarde, ordenação em relação a network-online.target e um atraso aleatório para que uma centena de servidores não inicie tudo no mesmo segundo. Quando o seu job precisa de alguma destas capacidades, um serviço e um temporizador do systemd numa VPS dá menos trabalho do que justificar uma linha do crontab. Escrever a parte do serviço obriga-o a responder a uma pergunta que o cron nunca faz: como é que a unidade sabe que o trabalho começou efetivamente? Por isso, leia primeiro o significado de Type= nas unidades simple, forking e notify, porque um script que se transforma num daemon com o tipo predefinido deixa a unidade com estado ativo, mas sem nenhum processo a executá-lo. O comportamento de novas tentativas também pertence à unidade, porque as políticas de reinício do systemd determinam o que acontece depois de uma falha, e o cron não tem qualquer resposta para essa questão.

Mantenha o cron para os jobs pequenos. Transfira para um temporizador tudo o que tenha dependências ou uma política de novas tentativas. Ambos podem ser executados no mesmo servidor, por isso não precisa de concluir esta migração de uma só vez.

FAQ

Por que o meu cron job funciona manualmente, mas falha quando é executado pelo cron?

Porque o ambiente da sua shell e o ambiente do cron são diferentes. A sua shell de login lê /etc/profile e ~/.bashrc, que definem PATH, o locale e as variáveis do seu agente. O cron inicia o comando sem nada disso, a partir de outro diretório de trabalho e, por vezes, com outra shell. Use caminhos absolutos para todos os comandos, defina o que for necessário no início do crontab ou dentro do script e agende um job de teste de um minuto que execute env | sort, pwd e id num ficheiro de log. Assim, pode consultar o ambiente real do cron em vez de o deduzir.

Como verifico se o cron executou efetivamente o meu job?

Leia o log do daemon. Use journalctl -u cron no Debian e no Ubuntu, ou journalctl -u crond no Fedora, Rocky e Alma. Algumas imagens encaminham as mensagens através do rsyslog para um ficheiro em /var/log. Procure uma entrada no minuto indicado pela sua agenda e confirme que ela identifica o seu comando. A ausência de uma entrada significa que o cron nunca recebeu essa agenda. Confirme se editou o crontab correto. Uma entrada sem resultado significa que o comando iniciou e terminou com erro. Nesse caso, capture a saída com um redirecionamento.

Por que date +%Y falha dentro de um crontab?

O cron trata % como um caractere especial no campo do comando. O primeiro % não escapado termina o comando. Tudo o que vem depois é enviado para esse comando como entrada padrão, e cada % adicional transforma-se numa nova linha. Por isso, um nome de ficheiro formatado com uma data nunca chega ao programa para o qual foi criado. Escape cada sinal de percentagem como \% ou mova o comando para um script e invoque o script a partir do cron. Dentro de um script, o sinal de percentagem não tem significado especial.

Para onde vai a saída do meu cron job?

Vai para o sistema de correio local, endereçada ao proprietário do crontab ou ao destinatário indicado por MAILTO. A maioria das imagens de VPS não tem um agente de transferência de correio instalado. Por isso, a mensagem é descartada e o job parece não produzir saída. Redirecione a saída para um ficheiro com >> /path/to/log 2>&1, mantendo essa ordem para que o erro padrão siga a saída padrão, ou encaminhe-a através de logger -t myjob e leia-a novamente com journalctl -t myjob. Não use > /dev/null 2>&1 enquanto ainda estiver a depurar o problema.

Devo usar cron ou um temporizador do systemd?

Use o cron para um comando simples executado a uma hora fixa, sobretudo se precisar de o mover para uma máquina que não execute systemd. Use um temporizador quando quiser a saída no journal sem redirecionamento, um estado de saída consultável, ordenação depois de a rede estar disponível, um atraso de arranque aleatório ou uma política de novas tentativas após uma falha. Ambos podem ser executados no mesmo servidor. Assim, pode migrar os jobs um de cada vez, quando isso for necessário.