EQVPS
PainelComeçar

Como agendar tarefas num VPS com cron e timers do systemd

Rode backups, limpezas e scripts num horário. Cron em cinco minutos com as três armadilhas que quebram tarefas em silêncio, depois timers do systemd para logs, recuperação após uma parada e hora exata. Cada comportamento abaixo foi testado num servidor Ubuntu 24.04 novo.

Um servidor se paga fazendo coisas enquanto você dorme: backups, rotação de logs, renovação de certificados, um relatório às nove. O Linux tem dois agendadores para isso. O cron tem quarenta anos e ocupa uma linha. Os timers do systemd ocupam dois arquivos pequenos e dão em troca logs, recuperação após paradas e hora exata. Este guia cobre os dois no Ubuntu 24.04, com cada comportamento verificado num servidor novo da nossa imagem, incluindo os três erros que fazem uma tarefa do cron falhar sem dizer nada.

Você precisa de um usuário com sudo; o guia de configuração do Ubuntu cria um.

1. Cron em cinco minutos

O cron vem instalado e rodando nas nossas imagens Ubuntu e Debian. Cada usuário tem uma tabela de tarefas; edite a sua com:

crontab -e

Cada linha tem cinco campos de tempo e um comando:

# min hour day month weekday  command
  30  3    *   *     *        /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
  */5 *    *   *     *        /usr/local/bin/check.sh
  0   9    *   *     1-5      /usr/local/bin/report.sh
  @reboot                     /usr/local/bin/start-tunnel.sh

Ou seja: às 03:30 toda noite, a cada 5 minutos, às 09:00 em dias úteis e uma vez após cada boot. */5 significa um a cada cinco valores, 1-5 um intervalo, 1,15 uma lista. Vale o fuso horário do servidor, UTC nas nossas imagens; defina o seu uma vez com o guia de nome do host e fuso horário e o cron segue.

Veja o que rodou com journalctl -u cron. Cada execução registra o usuário e o comando, e é aí que aparecem as três armadilhas.

Armadilha 1: a saída some. O cron envia por e-mail a saída de uma tarefa ao usuário. Nossa imagem não tem servidor de e-mail, então o journal dizia (CRON) info (No MTA installed, discarding output) e a saída sumia. Redirecione você mesmo, como no primeiro exemplo com >> /var/log/backup.log 2>&1.

Armadilha 2: o sinal de porcentagem. Num crontab, % encerra o comando e transforma o resto em entrada padrão. Agendamos date +%F > /tmp/today. O journal mostra o que o cron rodou de fato: CMD (date +). Nenhum arquivo foi gravado e nenhum erro chegou até nós. Escreva \%, ou coloque o comando num script e agende o script.

Armadilha 3: um ambiente diferente. O cron roda tarefas com /bin/sh, não bash, no seu diretório pessoal e com ambiente enxuto. No Ubuntu 24.04 o PATH foi melhor que a sua fama: o caminho completo do sistema, incluindo /usr/local/bin e /snap/bin, para root e para um usuário comum. O que ainda falta é tudo o que fica no seu diretório pessoal: ~/.local/bin, o node do nvm, pipx, cargo. Use caminhos completos na tarefa ou comece a linha com . $HOME/.profile;.

Tarefas que pertencem ao sistema e não a um usuário vão em /etc/cron.d/, como arquivo com um campo a mais, o usuário:

30 3 * * * root /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

O Ubuntu também roda tudo em /etc/cron.hourly, cron.daily, cron.weekly e cron.monthly, aos 17 minutos de cada hora, às 06:25, às 06:47 aos domingos e às 06:52 no dia 1. Coloque ali um script executável e ele roda junto com os outros.

2. Timers do systemd

Um timer são duas unidades: um serviço que faz o trabalho e um timer que diz quando. Crie primeiro o script, depois as unidades.

sudo nano /usr/local/bin/backup.sh
sudo chmod +x /usr/local/bin/backup.sh
sudo nano /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
sudo nano /etc/systemd/system/backup.timer
[Unit]
Description=Run the backup every night at 03:30

[Timer]
OnCalendar=*-*-* 03:30
Persistent=true
AccuracySec=1m

[Install]
WantedBy=timers.target

Ative o timer, não o serviço:

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers backup.timer

O list-timers mostra a próxima execução, quanto falta e a última execução. Logs não precisam de configuração: journalctl -u backup.service tem cada início, cada fim e cada linha de saída, com horário. Para rodar a tarefa agora, sudo systemctl start backup.service; é também assim que você testa o script antes de confiar no horário.

Sintaxe do calendário. OnCalendar se lê diadasemana ano-mês-dia hora:minuto:segundo, com * para qualquer valor. Teste uma expressão antes de usar:

systemd-analyze calendar "Mon..Fri *-*-* 03:00" --iterations 2

Ele imprime a forma normalizada e as próximas datas. Algumas expressões que verificamos:

OnCalendarSignificado
*-*-* 03:30Todo dia às 03:30
Mon..Fri *-*-* 09:00Dias úteis às 09:00
*:0/15A cada 15 minutos
weeklySegundas às 00:00
*-*-01 04:00Dia 1 de cada mês às 04:00
*-*-* 03:00 Europe/Kyiv03:00 no horário de Kyiv; rodou às 01:00 UTC no nosso teste

As três configurações que vale conhecer. Persistent=true grava a última execução em disco e recupera uma execução perdida enquanto o servidor estava desligado ou o timer parado. Paramos nosso timer de dois em dois minutos, esperamos passar uma execução agendada e o iniciamos de novo: a execução perdida disparou no mesmo segundo. Sem Persistent, ele simplesmente espera o próximo horário.

AccuracySec é a surpresa. Por padrão o systemd agrupa os timers numa janela de um minuto para economizar despertares, então nossa primeira execução veio às 07:42:18, não às 07:42:00. Com AccuracySec=1s as seguintes caíram em 07:46:00 e 07:48:00 no segundo exato. Ajuste quando o minuto importar; deixe o padrão para um backup noturno.

RandomizedDelaySec=10m faz o oposto de propósito: espalha o início numa janela para que dez servidores não batam no mesmo armazenamento de backup no mesmo segundo.

Para uma tarefa avulsa, nenhum arquivo:

sudo systemd-run --on-active=30m --unit=cleanup-once /usr/local/bin/cleanup.sh

Isso cria um timer transitório que dispara uma vez e some; até lá, o systemctl list-timers o mostra.

3. Qual usar

CronTimer do systemd
Configuração1 linha2 arquivos
LogsSó se você redirecionar a saídajournalctl -u name.service
Execução perdida com o servidor desligadoPerdidaRoda no boot com Persistent=true
Tarefa ainda rodando no próximo cicloUma segunda cópia iniciaPulada até a primeira terminar
Hora exataNo minutoNo segundo com AccuracySec=1s
StatusNenhumlist-timers, systemctl status

Use o cron para uma linha rápida que você vai reconhecer daqui a um ano. Use um timer para tudo cuja falha você precisa ver, que não pode se sobrepor ou que precisa rodar depois de um reboot perdido: backups, renovação de certificados, limpeza de bancos de dados. Se uma tarefa do cron pode se sobrepor, envolva-a: flock -n /tmp/backup.lock /usr/local/bin/backup.sh, e a segunda cópia sai na hora.

Os dois agendadores rodam uma tarefa e param. Para um programa que deve rodar o tempo todo e voltar depois de uma falha, você precisa de um serviço, não de um horário; veja como criar um serviço do systemd e, para Windows, como rodar um script 24/7 no Windows Server. Um bom primeiro timer é o do nosso guia de backup com restic; as atualizações de segurança automáticas são um timer que o Ubuntu já traz, apt-daily-upgrade.timer, um dos 17 que rodam num servidor novo.

Perguntas frequentes

Por que minha tarefa funciona no terminal mas não no cron?

Três motivos comuns. O cron roda a tarefa com sh, não bash, então sintaxe exclusiva do bash falha. Ferramentas instaladas no seu diretório pessoal, como nvm, pipx ou cargo, não estão no PATH do cron, então use caminhos completos. E qualquer % na linha de comando encerra o comando: date +%F virou date + no nosso servidor. Escape como \% ou mova o comando para um script.

Para onde vai a saída do cron?

Para o e-mail, por padrão. Nossa imagem não tem servidor de e-mail, então o cron registra 'No MTA installed, discarding output' e a saída se perde. Acrescente >> /var/log/myjob.log 2>&1 à tarefa, ou mude para um timer do systemd, cuja saída vai sozinha para o journal.

Qual a vantagem de um timer do systemd sobre o cron?

Logs no journalctl, nenhuma segunda cópia de uma tarefa ainda em execução, recuperação com Persistent=true das execuções perdidas enquanto o servidor estava desligado, e uma linha de status com a próxima e a última execução. O cron ganha na brevidade: uma linha em vez de dois arquivos.

Meu timer disparou 18 segundos atrasado. Por quê?

Por padrão o systemd agrupa os timers numa janela de um minuto para economizar despertares, que é a configuração AccuracySec. Nosso timer de dois em dois minutos disparou às 07:42:18 em vez de 07:42:00. Com AccuracySec=1s ele disparou às 07:46:00 em ponto. Ajuste quando o minuto exato importar.

Qual fuso horário o cron e os timers usam?

O do servidor, que é UTC nas nossas imagens. Defina uma vez com timedatectl, veja nosso guia de nome do host e fuso horário, e cron e timers o seguem. Um timer também pode ter seu próprio fuso: OnCalendar=*-*-* 03:00 Europe/Kyiv rodou às 01:00 UTC no nosso teste.

Comentários

Nenhum comentário ainda. Seja o primeiro.

Deixe um comentário

Os comentários são moderados antes de aparecerem.