Inspired by:
See Also:
Inspired by:
See Also:
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| # dataset to backup | |
| dataset="my-pool/data" | |
| # destroy leftover snapshots, usually already cleaned up by | |
| for snap in $(zfs list -rt snap -Ho name "${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 | |
| # create temporary snapshot for backup | |
| snapshot="restic-$(cat /proc/sys/kernel/random/uuid)" | |
| zfs snap -r "${dataset}@$snapshot" | |
| # clean up temporary snapshot | |
| _clean() { | |
| zfs destroy -r "${dataset}@$snapshot" && echo "zfs snapshot '${dataset}@$snapshot' destroyed" | |
| } | |
| trap _clean EXIT | |
| # get snapshot mountpoints of (sub) datasets | |
| restic_args=() | |
| for ds in $(zfs list -r -Ho name "${dataset}"); do | |
| if [[ "$(zfs get -Ho value mountpoint "$ds")" == "none" ]]; then | |
| continue | |
| fi | |
| path=$(findmnt -nr -o target -S "$ds") | |
| snap="${path}/.zfs/snapshot/$snapshot" | |
| restic_args+=( "${snap}" ) | |
| done | |
| # backup temporary snapshot | |
| restic backup \ | |
| --exclude-caches \ | |
| "${restic_args[@]}" |
| #!/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" |
@awehrfritz I wanted to strip away \var\tmp\zsnapmounts from the path recognized by restic. There's a pushd in line 59, so the paths passed to restic are relative.
There's a
pushdin line 59, so the paths passed to restic are relative.
Arrrg, I missed that one. Good point, it’s nice to strip away the tmp path!
Thanks for clarifying!
Thanks for this script @stackcoder! I was wondering if line 49 is indeed correct, or if it should rather be
snap_mnts+=( "${root_mnt}/$ds" )instead of
snap_mnts+=( "$ds" )I might be missing something here though.