#!/bin/bash # backup.sh is a simple example of how to safely backup the internally # consistent state of a system installed to btrfs to some other # location. It uses snapshots and rsync, and thus "$target" is any # destination supported by rsync. It is a full-system backup utility # that assumes superuser capabilities. # # Please report any issues with backup.sh using 'reportbug btrfs-progs' # # © 2015,2026 Nicholas D Steeves # SPDX-License-Identifier: GPL-2.0-only fsroot="/btrfs-admin" snapdir="$fsroot/snapshots" #sources=(@rootfs) #sources=(@home @) #target="/mnt/usb-disk/laptop-LATEST-RSYNC" #target="root@nas:/backups/laptop-LATEST-RSYNC" # Change "zstd" to your choice of rsync-suppported compression # algorithm, set to "" to autonegotiate, or set to "none" for no # compression. compression="zstd" check_prerequisites(){ if [[ ! -v fsroot ]] || [[ ! -v snapdir ]] || [[ ! -v sources ]] || [[ ! -v target ]] || [[ ! -v compression ]]; then echo -en "\n " realpath "${BASH_SOURCE:-$0}" echo " has incomplete configuration." echo -e " Aborting now.\n" exit 1 fi if ! findmnt --noheadings --nofsroot "$fsroot"; then if [ ! -t 0 ]; then echo "$fsroot does not exist and this is a noninteractive terminal." echo "Aborting now." exit 1 else while true; do echo -n "Filesystem root not found at $fsroot! Create? (yes/no): " read -r choice case "$choice" in yes | y | Yes | YES ) break ;; no | n | No | NO ) echo "Cannot continue without fsroot. Exiting now." exit 1 ;; * ) echo -e "Please answer yes or no.\n" ;; esac done echo "Creating $fsroot mount point with safe permissions." install -d -m 700 "$fsroot" mount "$(findmnt --noheadings --nofsroot -o SOURCE /)" "-o" "subvolid=5,noatime" "$fsroot" fi fi } fail(){ local exit_code="$1" echo "Something failed in $2()...exiting now" exit "$exit_code" } backup(){ rsync --acls --archive --hard-links --sparse --xattrs \ --compress-choice="$compression" --delete \ --info=progress2 \ ./ "$target" \ || fail "$?" "${FUNCNAME[0]}" } cleanup(){ echo "Expunging temporary snapshots created for the following: $*" btrfs sub del -c "$@" >/dev/null || fail "$?" "${FUNCNAME[0]}" sync rmdir "$snapdir/backup.sh-$epoch" } snapshot(){ mkdir -p "$snapdir/backup.sh-$epoch" || fail "$?" "${FUNCNAME[0]}" cd "$_" || fail "$?" "${FUNCNAME[0]}" for i in "$@"; do echo "Creating read-only snapshot $snapdir/backup.sh-$epoch/$i" btrfs sub snap -r ../../"$i" . >/dev/null || fail "$?" "${FUNCNAME[0]}" done } epoch="$(date "+%s")" check_prerequisites snapshot "${sources[@]}" backup cleanup "${sources[@]}"