Uma rotação cron mal configurada que sobrescreve os arquivos em vez de os incrementar é uma das formas clássicas de perder semanas de registos do servidor sem dar conta. Automatizar corretamente os backups Linux com cron e rsync evita precisamente essa classe de erros. Este guia documenta uma configuração cron + rsync testada em campo para 2026, armadilhas comuns incluídas.
A premissa é simples: a automação não é opcional nos backups. Os humanos esquecem-se. O cron não. Um bom sistema automatizado corre todas as noites às 02:00 sem que ninguém pense nisso — e é exatamente então que compensa, no dia em que uma base de dados fica corrompida ou alguém apaga sem querer uma grande diretoria de arquivos.
Porquê automatizar os backups
A resposta curta: porque a memória humana é um péssimo agendador para tarefas recorrentes críticas.
Os humanos esquecem-se, os scripts não. O inquérito anual de 2024 da Backblaze sobre hábitos de backup revela que 67% dos utilizadores que afirmam «fazer backup regularmente» fazem-no na realidade de forma irregular — com intervalos de 2 a 6 semanas entre backups. A regularidade percebida é sistematicamente superior à real. Uma tarefa cron diária às 02:00 corre sem exceção: durante as férias, fins de semana, noites com flutuações de energia (se o servidor tiver uma UPS).
O backup incremental torna a automação prática. Sem o rsync e as suas transferências delta, fazer backup de 500 GB de dados todas as noites seria proibitivo (50 a 100 GB de transferência de rede para uma diretoria de fotos padrão). Com o rsync, apenas os bytes alterados desde o último backup são transferidos. Para um backup noturno típico para um NAS em LAN, a cópia completa inicial pode demorar algumas horas, mas cada execução seguinte costuma concluir-se em minutos porque só os blocos alterados passam pelo cabo.
Escalar para várias máquinas. Passar de um para vários servidores torna o backup manual um procedimento longo que nunca é feito corretamente. Um script cron centralizado que recolhe os backups de todas as máquinas para um único destino exige zero tempo humano.
A monitorização automática deteta falhas. Um backup que falha em silêncio há 3 semanas é mais perigoso do que um backup nunca configurado — pelo menos aí sabe que não existe. Com um ping ao Healthchecks.io no fim do script, recebe um e-mail no momento em que um backup falha ou não corre como previsto.
rsync em 2026: sintaxe e opções essenciais
O rsync é uma ferramenta de sincronização incremental de ficheiros desenvolvida por Andrew Tridgell em 1996. O seu algoritmo de transferência delta transfere apenas os blocos modificados de um ficheiro em vez do ficheiro completo — é este o fundamento da sua eficiência nos backups diários.
Sintaxe básica:
rsync -avz --delete SOURCE DESTINATION
Opções explicadas:
-a(archive): preserva permissões, timestamps, symlinks, dono, grupo. Equivale a-rlptgoD.-v(verbose): mostra os ficheiros transferidos.-z(compress): ativa a compressão durante a transferência. Útil em WAN, inútil em LAN Gigabit.--delete: remove do destino os ficheiros que já não existem na origem.
Backup local para NAS:
rsync -av --delete /home/eric/ /mnt/nas/backup/eric-home/
Backup para servidor remoto via SSH:
rsync -avz --delete -e "ssh -i /home/eric/.ssh/backup_key -p 22" \
/var/www/ \
backup@192.168.1.100:/data/backups/www/
Opções avançadas em 2026:
# --mkpath: create destination directories if absent (recent flag, rsync 3.2.3+)
rsync -av --mkpath /source/ user@host:/path/that/does/not/exist/
# --exclude: skip specific patterns
rsync -av --exclude='*.log' --exclude='tmp/' --exclude='.git/' /source/ /dest/
# --bwlimit: cap bandwidth (in KB/s)
rsync -av --bwlimit=20000 /source/ user@host:/dest/
# --checksum: force hash-based verification (slower but more reliable)
rsync -avc --checksum /source/ /dest/
# Dry run: simulate without changing anything
rsync -avn --delete /source/ /dest/
Exemplo completo: backup de um VPS para uma Hetzner Storage Box:
rsync -avz --delete \
--exclude='*.sock' \
--exclude='/proc' \
--exclude='/sys' \
--exclude='/dev' \
--exclude='/run' \
--bwlimit=30000 \
-e "ssh -i /root/.ssh/hetzner_backup -p 23" \
/etc/ /home/ /var/www/ /var/backups/ \
u123456@u123456.your-storagebox.de:/backups/vps-main/
Um script como este, agendado para a noite, costuma transferir apenas os dados alterados para um destino remoto como uma Hetzner Storage Box (preço 2026: 3,81 €/mês por 100 GB).
Cron: sintaxe completa e exemplos práticos
O cron é o agendador de tarefas padrão do Unix, presente em todas as distribuições Linux. O comando crontab -e edita a tabela cron do utilizador atual.
A sintaxe de cinco campos:
# ┌───── minute (0 - 59)
# │ ┌───── hour (0 - 23)
# │ │ ┌───── day of month (1 - 31)
# │ │ │ ┌───── month (1 - 12)
# │ │ │ │ ┌───── day of week (0 - 7, 0 and 7 = Sunday)
# │ │ │ │ │
# * * * * * command
Exemplos de agendamento comuns:
# Every minute (testing / monitoring)
* * * * * /usr/local/bin/monitor.sh
# Every hour at H:00
0 * * * * /usr/local/bin/hourly-backup.sh
# Every day at 02:00
0 2 * * * /usr/local/bin/daily-backup.sh
# Every Sunday at 03:00
0 3 * * 0 /usr/local/bin/weekly-backup.sh
# 1st of every month at 04:00
0 4 1 * * /usr/local/bin/monthly-backup.sh
# Every 6 hours
0 */6 * * * /usr/local/bin/incremental.sh
# Weekdays (Mon-Fri) at 08:30
30 8 * * 1-5 /usr/local/bin/workday-sync.sh
Variáveis de ambiente importantes na crontab:
# cron does not inherit user PATH — always define it explicitly
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=admin@mydomain.com
# Explicit timezone (avoids scheduling surprises)
CRON_TZ=America/New_York
0 2 * * * /usr/local/bin/daily-backup.sh >> /var/log/backup.log 2>&1
systemd-timers: a alternativa moderna
Em sistemas com systemd (Ubuntu 16.04+, Debian 9+, CentOS 7+), os timers systemd oferecem melhor rastreabilidade:
# /etc/systemd/system/backup-daily.timer
[Unit]
Description=Daily rsync backup
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true # Catches up missed jobs if server was off
Unit=backup-daily.service
[Install]
WantedBy=timers.target
# /etc/systemd/system/backup-daily.service
[Unit]
Description=Daily rsync backup
After=network.target
[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/daily-backup.sh
Nice=19
IOSchedulingClass=idle
# Activation
systemctl enable backup-daily.timer
systemctl start backup-daily.timer
systemctl list-timers # Check status
journalctl -u backup-daily.service # View logs
A vantagem chave de Persistent=true: se o servidor estava desligado às 02:00, a tarefa corre no arranque seguinte. O cron padrão falha as tarefas se a máquina estiver offline.
Script de backup completo: registo, tratamento de erros, alertas
Eis um script de referência pronto para produção que pode adaptar. Cobre registo estruturado, tratamento de erros e notificações.
#!/bin/bash
# /usr/local/bin/daily-backup.sh
# Daily rsync backup with logging and alerts
set -euo pipefail
# ── Configuration ──────────────────────────────────────────────────────────────
BACKUP_SOURCE="/home /etc /var/www /var/backups"
BACKUP_DEST="/mnt/nas/backups/vps-main"
LOG_FILE="/var/log/backup-daily.log"
MAX_LOG_SIZE_MB=50
LOCK_FILE="/tmp/backup-daily.lock"
HEALTHCHECK_URL="https://hc-ping.com/YOUR-UUID-HERE" # Healthchecks.io
NOTIFY_EMAIL="admin@mydomain.com"
RSYNC_OPTIONS="-avz --delete --delete-after --exclude='*.sock' --exclude='*.pid'"
SSH_KEY="/root/.ssh/backup_key"
REMOTE_HOST="backup@192.168.1.100"
REMOTE_PATH="/data/backups"
BWLIMIT=30000 # KB/s — 30 MB/s cap
# ── Functions ──────────────────────────────────────────────────────────────────
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
send_alert() {
local subject="$1"
local body="$2"
echo "$body" | mail -s "$subject" "$NOTIFY_EMAIL" 2>/dev/null || true
# Healthchecks.io: ping /fail to signal failure
curl -fsS --retry 3 --max-time 10 "${HEALTHCHECK_URL}/fail" \
--data-raw "$body" > /dev/null 2>&1 || true
}
rotate_log() {
local size_mb
size_mb=$(du -sm "$LOG_FILE" 2>/dev/null | cut -f1 || echo 0)
if [ "$size_mb" -gt "$MAX_LOG_SIZE_MB" ]; then
mv "$LOG_FILE" "${LOG_FILE}.$(date +%Y%m%d)"
gzip "${LOG_FILE}.$(date +%Y%m%d)" 2>/dev/null || true
log "Log rotated (size threshold exceeded)"
fi
}
cleanup() {
rm -f "$LOCK_FILE"
}
# ── Preliminary checks ─────────────────────────────────────────────────────────
# Lock: prevent parallel executions
if [ -f "$LOCK_FILE" ]; then
log "ERROR: backup already running (lockfile present). Aborting."
send_alert "[BACKUP] Lock conflict on $(hostname)" \
"Backup was already running. Check PID in $LOCK_FILE."
exit 1
fi
trap cleanup EXIT
echo $$ > "$LOCK_FILE"
rotate_log
log "═══ Starting daily backup ═══"
# Check destination connectivity
if ! ssh -i "$SSH_KEY" -o ConnectTimeout=10 -o BatchMode=yes \
"$REMOTE_HOST" "echo OK" > /dev/null 2>&1; then
log "ERROR: cannot reach $REMOTE_HOST"
send_alert "[BACKUP] Destination unreachable on $(hostname)" \
"SSH to $REMOTE_HOST failed. Check network/key."
exit 2
fi
# ── rsync execution ────────────────────────────────────────────────────────────
ERRORS=0
START_TIME=$(date +%s)
for SOURCE_DIR in $BACKUP_SOURCE; do
if [ ! -d "$SOURCE_DIR" ]; then
log "WARNING: source directory absent: $SOURCE_DIR"
continue
fi
DEST_DIR="${REMOTE_PATH}/$(basename "$SOURCE_DIR")"
log "Syncing: $SOURCE_DIR → $REMOTE_HOST:$DEST_DIR"
if rsync $RSYNC_OPTIONS \
--bwlimit="$BWLIMIT" \
-e "ssh -i $SSH_KEY -o BatchMode=yes" \
"$SOURCE_DIR/" \
"$REMOTE_HOST:$DEST_DIR/" \
>> "$LOG_FILE" 2>&1; then
log "OK: $SOURCE_DIR synced"
else
log "ERROR: rsync failed for $SOURCE_DIR (exit code: $?)"
ERRORS=$((ERRORS + 1))
fi
done
# ── Final result ───────────────────────────────────────────────────────────────
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
DURATION_MIN=$((DURATION / 60))
if [ "$ERRORS" -eq 0 ]; then
log "Backup completed successfully in ${DURATION_MIN} min"
# Healthchecks.io success ping
curl -fsS --retry 3 --max-time 10 "$HEALTHCHECK_URL" > /dev/null 2>&1 || true
else
log "Backup completed with $ERRORS error(s) in ${DURATION_MIN} min"
send_alert "[BACKUP] $ERRORS error(s) on $(hostname)" \
"Backup finished with $ERRORS error(s). Duration: ${DURATION_MIN} min. See $LOG_FILE."
fi
log "═══ Backup complete ═══"
Adicionar à crontab:
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 2 * * * /usr/local/bin/daily-backup.sh
Como o rsync transfere apenas o que mudou, um backup incremental de algumas centenas de GB costuma demorar apenas alguns minutos por noite assim que a sincronização inicial estiver concluída.
rsnapshot: rotação automática de snapshots
O rsnapshot é um wrapper do rsync que implementa automaticamente a rotação de snapshots com hard links — cada snapshot parece uma cópia completa mas só guarda os ficheiros realmente novos ou modificados.
Instalação:
apt install rsnapshot # Debian/Ubuntu
yum install rsnapshot # CentOS/RHEL
Configuração /etc/rsnapshot.conf (excerto):
# IMPORTANT: tabs required between fields (not spaces)
config_version 1.2
# Snapshot root directory
snapshot_root /mnt/nas/rsnapshot/
# rsync command
cmd_rsync /usr/bin/rsync
# Rotation intervals
retain hourly 6 # 6 hourly snapshots
retain daily 7 # 7 days
retain weekly 4 # 4 weeks
retain monthly 12 # 12 months
# Global rsync options
rsync_short_args -az
rsync_long_args --delete --delete-excluded --numeric-ids
# Sources to back up
backup /home/eric/ localhost/
backup /etc/ localhost/
backup /var/www/ localhost/
backup root@192.168.1.10:/home/ web-server/
backup root@192.168.1.10:/etc/ web-server/
# Exclusions
exclude *.log
exclude tmp/
exclude .cache/
crontab do rsnapshot:
# Hourly snapshots (6am - 10pm)
0 6-22 * * * /usr/bin/rsnapshot hourly
# Daily at 02:30
30 2 * * * /usr/bin/rsnapshot daily
# Weekly on Monday at 03:00
0 3 * * 1 /usr/bin/rsnapshot weekly
# Monthly on 1st at 04:00
0 4 1 * * /usr/bin/rsnapshot monthly
Estrutura de diretorias resultante:
/mnt/nas/rsnapshot/
├── daily.0/ ← yesterday
│ ├── localhost/
│ │ ├── home/eric/
│ │ ├── etc/
│ │ └── var/www/
│ └── web-server/
├── daily.1/ ← two days ago
├── daily.2/
...
├── weekly.0/ ← last week
├── monthly.0/ ← last month
O restauro é trivial: cp -a /mnt/nas/rsnapshot/daily.2/localhost/home/eric/file.txt /home/eric/. Sem ferramenta especial, apenas uma cópia a partir de uma diretoria de snapshot.
Espaço em disco: com 180 GB de dados de origem e uma rotação de 7 diários + 4 semanais + 12 mensais = 23 snapshots, uso cerca de 320 GB no NAS (160 GB de dados «duplicados», porque os ficheiros inalterados partilham hard links entre snapshots).
BorgBackup: deduplicação e cifragem para backups sensíveis
O rsync destaca-se na sincronização de ficheiros. O BorgBackup é superior quando precisa de deduplicação ao nível do bloco, cifragem nativa e compressão variável. É a minha ferramenta para backups fora do local para uma Hetzner Storage Box e para backups que contêm dados pessoais.
Comparação rsync vs BorgBackup:
| Critério | rsync | BorgBackup |
|---|---|---|
| Deduplicação | Não (hard links via rsnapshot) | Sim (blocos variáveis ~512 KB) |
| Cifragem em repouso | Não | AES-256 nativa |
| Compressão | Durante a transferência (-z) | LZ4/ZSTD/ZLIB integrada |
| Restauro de um único ficheiro | Simples (cp a partir do snapshot) | borg extract |
| Espaço em disco | Maior (sem dedup real) | 40-60% menor em dados mistos |
| Complexidade de configuração | Baixa | Moderada |
Instalação e inicialização:
apt install borgbackup # Ubuntu 22.04: version 1.2.x
# Initialize an encrypted repository (recommended mode)
borg init --encryption=repokey-blake2 user@nas:/data/borg-repo/
# Store the passphrase in a password manager
# AND in a secure file outside the machine being backed up
Script de backup Borg com política de prune:
#!/bin/bash
export BORG_PASSPHRASE="YOUR_LONG_AND_COMPLEX_PASSPHRASE"
export BORG_REPO="user@nas:/data/borg-repo"
# Create snapshot with timestamp
borg create \
--verbose \
--compression lz4 \
--exclude-caches \
--exclude '/home/*/.cache' \
--exclude '/home/*/.local/share/Trash' \
"${BORG_REPO}::$(hostname)-$(date +%Y%m%d-%H%M%S)" \
/home /etc /var/www
# Retention policy: 7 daily + 4 weekly + 12 monthly
borg prune \
--verbose \
--list \
--keep-daily=7 \
--keep-weekly=4 \
--keep-monthly=12 \
"${BORG_REPO}"
# Integrity verification (recommended weekly)
# borg check "${BORG_REPO}"
Restauro:
# List archives
borg list "${BORG_REPO}"
# Restore a specific file
borg extract "${BORG_REPO}::server-20260608-020000" home/eric/documents/important.pdf
# Full restoration
borg extract "${BORG_REPO}::server-20260608-020000"
Quando preferir o Borg ao rsync:
- Dados sensíveis (documentos pessoais, bases de dados que contêm dados pessoais)
- Backups para armazenamento na nuvem ou hosts não fiáveis
- Volumes de dados com elevada redundância (fotos, código-fonte com histórico git)
- Necessidade de um histórico de snapshots comprimido em armazenamento limitado
Num destino remoto de 100 GB como uma Hetzner Storage Box, o Borg pode manter vários meses de snapshots deduplicados e comprimidos numa fração do espaço que as cópias rsync simples exigiriam para a mesma profundidade de histórico — tipicamente um rácio de compressão bastante abaixo de 1, consoante o grau de redundância dos dados de origem.
Monitorização e alertas: nunca assumir que o backup está a correr
Um backup que falha em silêncio há 3 semanas é um desastre à espera de acontecer. A monitorização não é opcional.
Healthchecks.io: a abordagem mais simples
O Healthchecks.io é um serviço de monitorização de cron baseado em pings. Crie uma verificação com o intervalo esperado (diário 24h + 1h de tolerância), adicione o ping no fim do seu script. Se o ping não chegar, recebe um e-mail.
# At the end of the backup script, on success:
curl -fsS --retry 3 --max-time 10 \
"https://hc-ping.com/YOUR-UUID" > /dev/null 2>&1 || true
# On failure, ping the /fail endpoint:
curl -fsS --retry 3 --max-time 10 \
"https://hc-ping.com/YOUR-UUID/fail" > /dev/null 2>&1 || true
Plano gratuito: 20 verificações. Suficiente para 4 servidores com monitorização diária + semanal. Plano Business a 20 $/ano para equipas.
Monitorização de registos com um simples grep:
# Crontab: check backup logs for errors every hour
0 * * * * grep -i "error\|fail" /var/log/backup-daily.log \
| tail -5 \
| mail -s "[$(hostname)] Backup errors" admin@mydomain.com 2>/dev/null || true
Script de verificação da atualidade do backup:
#!/bin/bash
# Verify that the most recent backup is less than 26 hours old
BACKUP_DIR="/mnt/nas/backups"
MAX_AGE_HOURS=26
find "$BACKUP_DIR" -name "*.log" -newer \
<(date -d "$MAX_AGE_HOURS hours ago") > /tmp/recent_backups 2>/dev/null
if [ ! -s /tmp/recent_backups ]; then
echo "ALERT: No recent backup on $(hostname)" \
| mail -s "[BACKUP] Backup too old!" admin@mydomain.com
fi
Prometheus + Grafana para homelabs avançados:
Para ambientes com vários servidores, o node_exporter do Prometheus expõe métricas do sistema de ficheiros. Uma regra do Alertmanager pode disparar uma notificação no Slack se o timestamp do último backup ultrapassar um limiar:
# prometheus/rules/backup.yml
groups:
- name: backup_freshness
rules:
- alert: BackupStaleness
expr: |
(time() - node_filesystem_file_content_mtime_seconds{
mountpoint="/mnt/nas",
path="/data/backups/vps-main"
}) > 90000
for: 1h
labels:
severity: warning
annotations:
summary: "Stale backup on {{ $labels.instance }}"
description: "Last backup {{ $value | humanizeDuration }} ago"
Um painel como este pode correr em hardware tão modesto como um Raspberry Pi, dando-lhe monitorização central para vários servidores a um custo de infraestrutura quase nulo (o Prometheus e o Grafana são ambos open source).
A automação cron + rsync é a camada técnica que torna prática à escala a estratégia de backup 3-2-1. Para perceber como isto encaixa numa arquitetura de backup completa, consulte o guia da estratégia de backup 3-2-1. Para utilizadores de Windows e Mac que gerem servidores Linux a par de máquinas de secretária, o guia de backup automático Windows e Mac cobre os equivalentes com interface gráfica.
Quando a prevenção falha e a recuperação de dados se torna necessária, o guia de recuperação de dados de disco rígido 2026 cobre as opções caseiras e profissionais, e a comparação dos melhores softwares de recuperação de dados 2026 avalia as ferramentas disponíveis. Para estimativas de custos de recuperação profissional, consulte o nosso guia de custos de recuperação de dados 2026.
Backup com interface gráfica automatizado para Windows e Mac
Se também gere máquinas Windows/Mac a par dos seus servidores Linux — o EaseUS Todo Backup automatiza os backups sem qualquer linha de comandos · Versão gratuita disponível
Recover your deleted files → EaseUS
Free scan · deleted, formatted & lost files · Windows & Mac
