Last active
April 2, 2016 20:15
-
-
Save kakra/5520370 to your computer and use it in GitHub Desktop.
Simple script for creating space-efficient backups to btrfs using rsync and snapshots
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Call the backup script with systemd, and since it is a background service, treat it like that by lowering IO and CPU priority | |
# so it will work in the background without disturbing your normal workflow. | |
# | |
# Author: Kai Krakow <[email protected]> | |
# License: GPL3 | |
[Unit] | |
Description=USB Backup Service | |
[Service] | |
Type=oneshot | |
ExecStart=/usr/local/sbin/usb-backup.sh | |
IOSchedulingClass=idle | |
IOSchedulingPriority=7 | |
CPUSchedulingPolicy=batch | |
Nice=3 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# Author: Kai Krakow <[email protected]> | |
# License: GPL3 | |
# | |
# Put something like this into your fstab: | |
# LABEL=usb-backup /mnt/private/usb-backup btrfs noauto,compress-force=zlib,subvolid=0,x-systemd.automount,x-systemd.idle-timeout=300 | |
# | |
# This script will backup the current system to a scratch area on a btrfs formatted backup drive, then takes a snapshot | |
# of it named by the timestamp when the backup started. | |
# | |
# Todo list (in order of priority): | |
# * deduplicate backups after snapshotting | |
# * ensure unmounting the filesystem after btrfs has done all its cleanup work (ie, finished removing old snapshots) | |
# * use a private mount to mount subvol=0 of all btrfs filesystems and backup these instead (get rid of the exclude patterns) | |
# ...that obviously only works right for single btrfs sources, figure out a nice way to support all fs types this way | |
# * support taking an atomic snapshot of the backup source first and use that as the backup source | |
# * support backup media rotation (so you could use two drives swapped every day, one attached, one kept off-site) | |
# * support remote backups | |
DATE=$(date +%Y%m%d-%H%M) | |
BASEDIR=/mnt/private/usb-backup | |
LOG=/var/log/usb-backup.log | |
KEEP_FREE=$((200*1024*1024)) | |
MIN_AGE=$(date +%Y%m%d-%H%M --date="1 month ago") | |
clean_snapshots() { | |
if [[ $(df "${BASEDIR}" | awk -e'/dev/ { print $4 }') -lt "${KEEP_FREE}" ]]; then | |
SNAPSHOT=$(ls ${BASEDIR}/snapshots/ | sort | head -1) | |
if [[ -z "${SNAPSHOT}" ]]; then return 0; fi | |
if [[ "$(basename "${SNAPSHOT}")" < "system-${MIN_AGE}" ]]; then | |
echo "Removing one old snapshot ${SNAPSHOT}..." | |
btrfs subvolume delete "${BASEDIR}/snapshots/${SNAPSHOT}" | |
fi | |
fi | |
} | |
take_snapshot() { | |
btrfs filesystem sync "${BASEDIR}" | |
echo "Creating backup snapshot ${DATE} of scratch area..." | |
btrfs subvolume snapshot -r "${BASEDIR}/current" "${BASEDIR}/snapshots/system-${DATE}" | |
clean_snapshots | |
echo "Ensuring backup filesystem sync..." | |
btrfs filesystem sync "${BASEDIR}" | |
sync | |
} | |
if [ -d "${BASEDIR}/snapshots" ]; then | |
if [ ! -e "${BASEDIR}/snapshots/system-${DATE}" ]; then | |
clean_snapshots | |
echo "Starting backup to USB disk scratch area..." | |
echo -n >${LOG} | |
if /usr/bin/systemd-inhibit --what=sleep rsync -aAXH --timeout=300 --delete --delete-excluded --inplace --no-whole-file --stats --log-file=${LOG} \ | |
--exclude "/dev/*" \ | |
--exclude "/lost+found/" \ | |
--exclude "/media/*" \ | |
--exclude "/mnt/*" \ | |
--exclude "/proc/*" \ | |
--exclude "/run/*" \ | |
--exclude "/sys/*" \ | |
--exclude "/tmp/*" \ | |
--exclude "/var/tmp/*" \ | |
--exclude "tmpfs" \ | |
--exclude "${LOG}" \ | |
--exclude "${BASEDIR}" \ | |
/ ${BASEDIR}/current/ | |
then | |
take_snapshot | |
else | |
if [ "$?" = 24 ]; then | |
echo "$0: Backup finished with warnings." >&2 | |
take_snapshot | |
else | |
echo "$0: Backup failed!" >&2 | |
exit 74 | |
fi | |
fi | |
echo "Waiting for snapshot cleanup..." | |
btrfs subvolume sync "${BASEDIR}" | |
else | |
echo "$0: Snapshot directory name already exists - skipping backup" >&2 | |
exit 69 | |
fi | |
else | |
echo "$0: USB backup drive not mounted - skipping backup" >&2 | |
exit 75 | |
fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Manual backups are like no backups at all, so here's a systemd timer unit to do automated unattended backups. This pretends that | |
# you have configured your backup drive for on-access automounting through systemd. This can also wake up your computer if you | |
# allow systemd to do so. You have to adjust sleep timeout to be longer than the backup takes to run - otherwise you are left with | |
# a powered-on system in the morning. | |
# | |
# Author: Kai Krakow <[email protected]> | |
# License: GPL3 | |
[Unit] | |
Description=Daily USB Backup Timer | |
[Timer] | |
OnCalendar=03:00 | |
[Install] | |
WantedBy=default.target |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment