#!/usr/bin/env bash set -euo pipefail # dataset to backup export ZFS_DATASET="my-pool/data" # destroy leftover snapshots, usually already cleaned up if [ -z "${EXEC_UNSHARED:-}" ]; then for snap in $(zfs list -rt snap -Ho name "${ZFS_DATASET}"); do if [[ "${snap}" =~ @restic-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$ ]]; then zfs destroy "${snap}" && echo "leftover zfs snapshot '${snap}' destroyed" fi done fi # create temporary snapshot for backup if [[ -z "${ZFS_SNAP:-}" ]]; then ZFS_SNAP="restic-$(cat /proc/sys/kernel/random/uuid)" zfs snap -r "${ZFS_DATASET}@${ZFS_SNAP}" # clean up temporary snapshot on exit _clean_snap() { zfs destroy -r "${ZFS_DATASET}@${ZFS_SNAP}" && echo "zfs snapshot '${ZFS_DATASET}@${ZFS_SNAP}' destroyed" } trap _clean_snap EXIT export ZFS_SNAP fi # mount snapshot and backup within private mount namespace if [ -z "${EXEC_UNSHARED:-}" ]; then export EXEC_UNSHARED=1 echo "re-executing '$0' in new private mount namespace.." unshare --mount --propagation private "$0" exit 0 fi # use tmpfs for further mountpoints root_mnt="${TMPDIR:-/var/tmp}/zsnapmounts" mount -t tmpfs -o X-mount.mkdir tmpfs "${root_mnt}" # mount snapshot of (sub) datasets snap_mnts=() for ds in $(zfs list -r -Ho name "${ZFS_DATASET}"); do if [[ "$(zfs get -Ho value mountpoint "$ds")" == "none" ]]; then continue fi mount -t zfs -o X-mount.mkdir "$ds@${ZFS_SNAP}" "${root_mnt}/$ds" snap_mnts+=( "$ds" ) echo "mounted dataset '$ds@${ZFS_SNAP}'" done # backup temporary snapshot echo -e "start restic backup\n" pushd "${root_mnt}" > /dev/null restic backup -v \ --exclude-caches \ "${snap_mnts[@]}" echo -e "\nrestic backup done"