The Problem#
Recently I lost a hard drive without warning. It wasn’t catastrophic because I had backups, but it made me aware that many hobbyists with home servers have no data protection strategy at all. If your Docker server crashes tomorrow, how long would it take you to recover it?
In this article I share how I automated backups of my Docker infrastructure using rsync and cron. It’s simple, efficient, and it works.
The Strategy#
My approach is straightforward:
- rsync to incrementally sync only what changed
- cron to automate daily execution
- An external USB drive as the backup destination
- Retention of multiple snapshots for granular recovery
This isn’t cloud backup. It’s local backup, fast, and under my control.
Step-by-Step Setup#
1. Prepare the Storage#
I connected a USB drive and mounted it at /mnt/backup. Verify it’s available:
lsblk
mount | grep backup2. Backup Script#
I created /usr/local/bin/docker-backup.sh:
#!/bin/bash
BACKUP_DEST="/mnt/backup"
DOCKER_DATA="/var/lib/docker"
COMPOSE_DIR="/home/user/docker-compose"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_PATH="$BACKUP_DEST/backups/$TIMESTAMP"
# Crear directorio de backup
mkdir -p "$BACKUP_PATH"
# Backup de volúmenes Docker
echo "[$(date)] Iniciando backup de Docker volumes..."
rsync -av --delete "$DOCKER_DATA/volumes/" "$BACKUP_PATH/volumes/" >> /var/log/docker-backup.log 2>&1
# Backup de configuraciones docker-compose
echo "[$(date)] Iniciando backup de docker-compose..."
rsync -av "$COMPOSE_DIR/" "$BACKUP_PATH/compose/" >> /var/log/docker-backup.log 2>&1
# Backup incremental de datos de aplicaciones
echo "[$(date)] Iniciando backup de datos..."
rsync -av --delete "/home/user/app-data/" "$BACKUP_PATH/app-data/" >> /var/log/docker-backup.log 2>&1
# Limpiar backups más antiguos (mantener 7 últimos)
echo "[$(date)] Limpiando backups antiguos..."
ls -t "$BACKUP_DEST/backups" | tail -n +8 | xargs -I {} rm -rf "$BACKUP_DEST/backups/{}"
echo "[$(date)] Backup completado" >> /var/log/docker-backup.logMake it executable:
chmod +x /usr/local/bin/docker-backup.sh3. Configure cron#
I edited the root user’s cron table:
sudo crontab -eI added this line to run at 2 AM every day:
0 2 * * * /usr/local/bin/docker-backup.shTo verify it’s registered:
sudo crontab -l4. Monitoring#
I created a second script to alert me if something fails. In /usr/local/bin/check-backup.sh:
#!/bin/bash
LAST_BACKUP=$(ls -t /mnt/backup/backups | head -1)
BACKUP_TIME=$(date -d "$(stat -c %y /mnt/backup/backups/$LAST_BACKUP | cut -d' ' -f1)" +%s)
CURRENT_TIME=$(date +%s)
DIFF=$((($CURRENT_TIME - $BACKUP_TIME) / 3600))
if [ $DIFF -gt 25 ]; then
echo "ALERTA: No hay backup desde hace $DIFF horas"
else
echo "Último backup: $DIFF horas atrás - OK"
fiI run it manually each week or via cron if I want:
chmod +x /usr/local/bin/check-backup.sh
/usr/local/bin/check-backup.shImportant Considerations#
Disk Space: rsync with --delete syncs the source exactly. I check that the destination has at least 1.5x the size of the Docker data.
Permissions: The script runs as root, so it can access /var/lib/docker. If you use a regular user, you’ll need special permissions.
Testing: Once a month I simulate a recovery by restoring a random file to a test machine. A backup that was never tested doesn’t exist.
Encryption: My USB drive is at home with me, so I don’t encrypt it. If you kept it elsewhere, consider --backup-dir with synchronization to an encrypted destination.
Result#
Now I sleep better. Every night at 2 AM, Docker, configurations, and data sync automatically. If the server dies, I recover everything in 30 minutes.
The key is: simple automation, manual verification. Don’t wait for something to fail to test your backup.
Recommended Equipment#
- Raspberry Pi 3 B+ — Lightweight, low-power server to start your homelab
- Raspberry Pi 4 (4GB) — The perfect foundation for homelab, Docker, and monitoring
Affiliate links. No extra cost to you.